2015-05-25 14 views
8

Come posso chiamare il metodo tupled su questa classe di casi?Classe caso Scala Tupled

case class(a: Int, b: String)(c: String, d: Int) 

Il motivo per cui ho la mia classe caso come questo è perché voglio usare solo i primi due parametri da considerare per eguali e confronto hashCode!

Quindi, come posso chiamare correttamente la tupla su una tale classe di casi?

+1

Perché vuoi che si tratti di una case class? Le classi di casi hanno alcune caratteristiche di convenienza, ma il recupero dell'investimento è una perdita di flessibilità. Basta definire la propria classe e sovrascrivere equamente/hashCode da soli. –

+0

Sì, capisco, ma eseguo alcuni pattern di corrispondenza pesante su questa classe di case, quindi devo lasciarlo come caso class! – sparkr

+1

Forse vuoi rinominare la tua domanda in modo simile a "Elenchi di parametri multipli di Classe Case Scala" invece di Tupled –

risposta

13

In breve, non sembra una buona idea utilizzare le case classes in questo modo. Ecco una spiegazione.

Controlliamo la dichiarazione della classe insieme a generata apply e unapply:

scala> case class A(a: Int, b: String)(c: String, d: Int) 
defined class A 

scala> A.apply _ 
res0: (Int, String) => (String, Int) => A = <function2> 

scala> A.unapply _ 
res1: A => Option[(Int, String)] = <function1> 

Si può vedere che, anche se apply prende 4 argomenti in totale (curry), unapply rigetti in mare secondo elenco.

Vediamo se siamo in grado di accedere ai membri della seconda lista:

scala> val a = A(1, "two")("three", 4) 
a: A = A(1,two) 

scala> a.a 
res2: Int = 1 

scala> a.c 
<console>:11: error: value c is not a member of A 
       a.c 
       ^

... no, non è un modo normale. Controlliamo un paio di altre proprietà:

scala> a.productArity 
res4: Int = 2 

scala> a.productIterator.toList 
res5: List[Any] = List(1, two) 

Ok, sembra che la seconda lista di argomenti sia praticamente ignorata. Scaviamo in:

scala> :javap A 
... 
    public int a(); 
    descriptor:()I 
    flags: ACC_PUBLIC 
    Code: 
     stack=1, locals=1, args_size=1 
     0: aload_0 
     1: getfield  #16     // Field a:I 
     4: ireturn 
... 
    public java.lang.String b(); 
    descriptor:()Ljava/lang/String; 
    flags: ACC_PUBLIC 
    Code: 
     stack=1, locals=1, args_size=1 
     0: aload_0 
     1: getfield  #21     // Field b:Ljava/lang/String; 
     4: areturn 
... 
    public boolean equals(java.lang.Object); 
    descriptor: (Ljava/lang/Object;)Z 
    flags: ACC_PUBLIC 
    ... //mentions only a and b:.... 
     32: invokevirtual #32     // Method a:()I 
     35: aload   4 
     37: invokevirtual #32     // Method a:()I 
     40: if_icmpne  88 
     43: aload_0 
     44: invokevirtual #35     // Method b:()Ljava/lang/String; 
     47: aload   4 
     49: invokevirtual #35     // Method b:()Ljava/lang/String; 
... 
    public A(int, java.lang.String, java.lang.String, int);                 
    descriptor: (ILjava/lang/String;Ljava/lang/String;I)V                 
    flags: ACC_PUBLIC                          
    Code:                             
     stack=2, locals=5, args_size=5                      
     0: aload_0                          
     1: iload_1                          
     2: putfield  #16     // Field a:I                 
     5: aload_0                          
     6: aload_2                          
     7: putfield  #21     // Field b:Ljava/lang/String;            
     10: aload_0                          
     11: invokespecial #100    // Method java/lang/Object."<init>":()V          
     14: aload_0                          
     15: invokestatic #106    // Method scala/Product$class.$init$:(Lscala/Product;)V      
     18: return                           
     LocalVariableTable:                         
     Start Length Slot Name Signature                    
      0  19  0 this LA;                     
      0  19  1  a I                      
      0  19  2  b Ljava/lang/String;                  
      0  19  3  c Ljava/lang/String;                  
      0  19  4  d I                     

Quindi non v'è alcun uso di c e d nel costruttore o in pari.

Si può ancora fare secondo elenco ARG params utile anteponendo val:

scala> case class B(a: Int, b: String)(val c: String, val d: Int)   
defined class B               

scala> val b = B(1, "two")("three", 4)          
b: B = B(1,two)               

scala> b.c                 
res6: String = three         

scala> b.d                 
res8: Int = 4  

Vediamo come l'uguaglianza e la hashcode funziona in questo caso:

scala> val b2 = B(1, "two")("no the same", 555) 
b2: B = B(1,two) 

scala> b == b2 
res10: Boolean = true 

scala> b.hashCode 
res13: Int = -1563217100 

scala> b2.hashCode 
res14: Int = -1563217100 

sembra funzionare nel modo che lo voglio, che personalmente non mi piace;)

Per completezza, la corrispondenza di modello predefinita è ancora come era per la classe A:

scala> B.unapply _ 
res15: B => Option[(Int, String)] = <function1> 

Specifiche linguaggio Scala spiega come funziona here.

I parametri formali nella prima sezione di parametro di una classe di caso sono denominati elementi; sono trattati appositamente. Innanzitutto, il valore di come parametro può essere estratto come campo di un modello di costruzione. In secondo luogo, un prefisso val è implicitamente aggiunto a tale parametro, a meno che il parametro abbia già un modificatore val o var. Quindi, viene generata una definizione di accesso per il parametro.

e

Ogni classe caso ignora implicitamente alcune definizioni dei metodi della classe scala.AnyRef a meno che una definizione dello stesso metodo è già dato nel caso classe stessa o di una definizione concreta della stessa il metodo viene fornito in alcune classi base della classe case diverse da AnyRef. In particolare:

  • Metodo uguale: (Qualunque) Booleana è uguaglianza strutturale, in cui due istanze sono uguali se entrambi appartengono alla classe caso in questione e hanno uguali (rispetto al eguali) costruttore argomenti (limitato agli elementi della classe, ovvero il primo parametro sezione).
  • Metodo hashCode: Int calcola un codice hash. Se i metodi hashCode dei membri della struttura dati corrispondono a valori uguali (rispetto ai valori uguali) a hash-code uguali, il metodo hashCode della classe case include anche .
  • Metodo toString: String restituisce una rappresentazione stringa che contiene il nome della classe e dei suoi elementi.
+0

Molto ben spiegato! – sparkr

+1

Solo per completezza (dato che questa risposta non menziona nemmeno 'tupla'), il modo per ottenere la funzione tupla, come richiesto, sarebbe qualcosa di simile:' val tupled = (A.apply _). Tupled.andThen (_.tupled) '. Nota comunque che ci vogliono ** due ** tuple come parametri (per esempio si farebbe: 'tupled ((123," foo ")) ((" bar ", 456))'). Trasformarlo in una funzione che richiede una sola tupla (nel tuo caso un 'Tuple4') non può essere ottenuto fuori dalla scatola senza destrutturare le tuple, quindi a quel punto dovresti anche scrivere il metodo tupla interamente manualmente. –

+2

Fondamentalmente: la magia della classe caso si applica solo alla prima lista di parametri. –