web-dev-qa-db-fra.com

Comment contourner la limite de classe de cas Scala de 22 champs?

Les classes de cas Scala ont une limite de 22 champs dans le constructeur. Je veux dépasser cette limite, existe-t-il un moyen de le faire avec l'héritage ou la composition qui fonctionne avec les classes de cas?

47
Phil

Plus récemment (octobre 2016, six ans après l'OP), le blog " Scala et 22 " de Richard Dallaway explore cette limite:

En 2014, lorsque Scala 2.11 a été publié, une limitation importante a été supprimée:

Case classes with > 22 parameters are now allowed. 

Cela dit, il existe toujours une limite sur le nombre de champs de classe de cas, veuillez consulter https://stackoverflow.com/a/55498135/1586965

Cela peut vous faire penser qu'il n'y a pas 22 limites dans Scala, mais ce n'est pas le cas. La limite persiste dans les fonctions et les tuples .

Le correctif ( PR 2305 ) introduit dans Scala 2.11 a supprimé la limitation pour les scénarios courants ci-dessus: construction de classes de cas, accès aux champs (y compris la copie) et correspondance de modèles ( découvrant les cas Edge ).

Il l'a fait en omettant unapply et tupled pour les classes de cas au-dessus de 22 champs.
En d'autres termes, la limite à Function22 et Tuple22 existe toujours.

Contourner la limite (post Scala 2.11)

Il existe deux astuces courantes pour contourner cette limite.

  • La première consiste à utiliser des tuples imbriqués .
    Bien qu'il soit vrai qu'un Tuple ne peut pas contenir plus de 22 éléments, chaque élément lui-même pourrait être un Tuple

  • L'autre astuce courante consiste à utiliser des listes hétérogènes (HLists), où il n'y a pas de limite 22.

Si vous souhaitez utiliser des classes de cas, il est préférable d'utiliser l'implémentation HList sans forme. Nous avons créé la bibliothèque Slickless pour vous faciliter la tâche. En particulier la récente méthode mappedWith convertit entre les classes HLists sans forme et les classes de cas. Cela ressemble à ceci:

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])
}

Il y a exemple complet avec 26 colonnes dans la base de code Slickless.

41
VonC

Ce problème va être corrigé dans Scala 2.11.

30
Brian

Construisez une classe normale qui agit comme une classe de cas.

J'utilise toujours scala 2.10.X car c'est la dernière version prise en charge par Spark, et dans Spark-SQL, j'utilise beaucoup les classes de cas.

La solution de contournement pour case classes avec plus de 22 champs:

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
    }
}
23

Il est intéressant que votre constructeur soit chargé, mais vous pouvez empaqueter les valeurs associées dans une classe de cas qui leur est propre.

Donc, alors que vous pourriez avoir

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

tu peux le faire

case class MyClass(address: Address)

Vous avez également d'autres options:

  • Grouper les éléments en tuples
  • Crée le tien Function23 trait (ou autre)
  • Utilisez curry

MISE À JOUR: Comme d'autres l'ont remarqué, ce n'est plus un problème après la sortie de Scala 2.11 - bien que j'hésiterais à utiliser le terme "correctif". Cependant, le "Catch 22", si vous le souhaitez, apparaît parfois dans des bibliothèques tierces Scala.

19
Vidya