Подтвердить что ты не робот

Как обойти ограничение класса case Scala из 22 полей?

Scala классы case имеют предел из 22 полей в конструкторе. Я хочу превзойти этот предел, есть ли способ сделать это с наследованием или композицией, которая работает с классами case?

4b9b3361

Ответ 1

Совсем недавно (октябрь 2016 года, через шесть лет после ОП) в блоге Ричарда Даллауэя " Scala and 22 " исследуется этот предел:

Еще в 2014 году, когда была выпущена Scala 2.11, было снято важное ограничение:

Case classes with > 22 parameters are now allowed. 

Тем не менее, по-прежнему существует ограничение на количество полей класса дел, см. fooobar.com/questions/17135565/...

Это может заставить вас думать, что в Scala нет 22 ограничений, но это не так. Предел живет в функциях и кортежах.

Исправление (PR 2305), представленное в Scala 2.11, сняло ограничение для вышеупомянутых распространенных сценариев: создание классов наблюдений, доступ к полям (включая копирование) и сопоставление с образцом (исключающие случаи).

Он сделал это, опуская unapply и tupled для case-классов выше 22 полей.
Другими словами, ограничение для Function22 и Tuple22 все еще существует.

Работа вокруг лимита (пост Scala 2.11)

Есть два распространенных способа обойти это ограничение.

  • Первый - использовать вложенные кортежи.
    Хотя его кортеж не может содержать более 22 элементов, каждый элемент может быть кортежем.

  • Другим распространенным приемом является использование гетерогенных списков (HLists), где нет 22 ограничений.

Если вы хотите использовать классы case, вам лучше использовать бесформенную реализацию HList. Мы создали библиотеку Slickless, чтобы сделать это проще. В частности, недавний метод mappedWith конвертирует между бесформенными HLists и case-классами. Это выглядит так:

import slick.driver.H2Driver.api._
import shapeless._
import slickless._

class LargeTable(tag: Tag) extends Table[Large](tag, "large") {
  def a = column[Int]("a")
  def b = column[Int]("b")
  def c = column[Int]("c")
  /* etc */
  def u = column[Int]("u")
  def v = column[Int]("v")
  def w = column[Int]("w")

  def * = (a :: b :: c :: /* etc */ :: u :: v :: w :: HNil)
    .mappedWith(Generic[Large])
}

Вот полный пример с 26 столбцами в базе кода Slickless.

Ответ 2

Этот issue будет исправлен в Scala 2.11.

Ответ 3

Создайте нормальный класс, который действует как класс case.

Я все еще использую scala 2.10.X, так как это последняя версия, поддерживаемая Spark, а в Spark-SQL я сильно использую классы case.

Обходной путь для case classes с более чем 22 полями:

class Demo(val field1: String,
    val field2: Int,
    // .. and so on ..
    val field23: String)

extends Product 
//For Spark it has to be Serializable
with Serializable {
    def canEqual(that: Any) = that.isInstanceOf[Demo]

    def productArity = 23 // number of columns

    def productElement(idx: Int) = idx match {
        case 0 => field1
        case 1 => field2
        // .. and so on ..
        case 22 => field23
    }
}

Ответ 4

Интересно, что ваш конструктор загружен, но вы можете упаковать связанные значения в собственный класс case.

Итак, если у вас есть

case class MyClass(street: String, city: String, state: String, zip: Integer)

вы можете сделать это

case class MyClass(address: Address)

У вас есть и другие варианты:

  • Элементы группы в кортежи
  • Создайте свой собственный признак Function23 (или что-то еще)
  • Используйте currying

ОБНОВЛЕНИЕ. Как отмечали другие, это уже не проблема после выпуска Scala 2.11 - хотя я бы смутился, чтобы использовать термин "исправить". Тем не менее, "Catch 22", если хотите, иногда появляется в сторонних библиотеках Scala.

Ответ 5

Когда у вас есть много значений, это обычно признак того, что ваш дизайн нуждается в переработке в любом случае.

Формируйте классы прерывистых случаев, которые затем объединяются в более крупные. Это также упрощает понимание кода, обоснование и поддержку кода. Помимо обхода этой проблемы у вас есть.

Например, если бы я хотел хранить пользовательские данные, я мог бы сделать это....

case class User(name: Name, email: String)
case class Name(first: String, last: String)

С такими немногими вещами это, конечно, не было бы необходимо. Но если у вас есть 22 вещи, которые вы пытаетесь втиснуть в один класс, вы все равно захотите сделать такой класс прерывистого случая.