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

Класовете на Scala case имат ограничение от 22 полета в конструктора. Искам да надвиша това ограничение, има ли начин да го направя с наследяване или композиция, която работи с класове case?


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


Отговори (5)


Съвсем наскоро (октомври 2016 г., шест години след OP), публикацията в блога „Ричард Далауей изследва това ограничение:

Още през 2014 г., когато беше пусната Scala 2.11, важно ограничение беше премахнато:

Case classes with > 22 parameters are now allowed. 

Въпреки това все още съществува ограничение за броя на полетата за класове на случай, моля, вижте 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
Extending Product ви дава хубавите части за итерация, но не получавате метода за копиране. Методът за копиране е в класове 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 (или каквото и да е)
  • Използвайте currying

АКТУАЛИЗАЦИЯ: Както други отбелязаха, това вече не е проблем след пускането на 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 изисква case клас с всички полета. - person Sohaib; 09.07.2015
comment
този отговор беше първата мисъл, която ми хрумна и е горе-долу правилен - person Dragonborn; 31.08.2018