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

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


person Phil    schedule 28.11.2013    source источник
comment
Да, ты прав. В некоторых случаях я использую неявный класс case, который использует меньше полей, чем реальный класс.   -  person Phil    schedule 28.11.2013


Ответы (5)


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

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

Case classes with > 22 parameters are now allowed. 

Тем не менее, по-прежнему существует ограничение на количество полей класса case, см. https://stackoverflow.com/a/55498135/1586965

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

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

Это было достигнуто за счет исключения unapply и tupled для классов дел более 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.

person VonC    schedule 11.10.2016

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

person Brian    schedule 28.11.2013
comment
scala 2.11 спасает жизни! - person soulmachine; 28.09.2016

Создайте нормальный класс, который действует как класс 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
    }
}
person Boggio    schedule 27.08.2015
comment
Расширение продукта дает вам хорошие части итерации, но вы не получаете метод копирования. Метод копирования есть в case-классах, но не в трейте Product, потому что он генерируется компилятором Scala (теперь он может быть строго типизирован для каждого поля в вашем case-классе). - person Samer Adra; 27.05.2016
comment
Какой сериализатор вы регистрируете в Spark? Я начал с twitter.chill.avro.AvroSerializer.SpecificRecordBinarySerializer, но в этом случае мой класс должен реализовать SpecificRecordBase, что снова ставит меня перед ограничением в 22 поля. - person Stuart; 20.07.2016

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

Так что, хотя у вас может быть

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

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

case class MyClass(address: Address)

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

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

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

person Vidya    schedule 28.11.2013

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

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

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

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

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

person Dante Romero    schedule 28.11.2013
comment
Существует один json, который необходимо проанализировать, который имеет более 22 полей. При использовании средства сопоставления объектов, такого как json4s, требуется случай класса со всеми полями. - person Sohaib; 09.07.2015
comment
этот ответ был первой пришедшей мне в голову мыслью и он более-менее правильный - person Dragonborn; 31.08.2018