2015-05-25 8 views
8

Как я могу вызвать метод tupled в этом классе case?Scala Case Class Tupled

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

Причина, почему у меня есть дело класса, как это происходит потому, что я хочу использовать только первые два параметра, которые необходимо учитывать для равных и сравнения Hashcode!

Итак, как я могу правильно называть чередование такого класса case?

+1

Почему вы хотите, чтобы это был класс дел? Классы классов имеют некоторые удобные функции, но окупаемость для этого - это некоторая потеря гибкости. Просто определите свой собственный класс и переопределите equals/hashCode самостоятельно. –

+0

Да, я понимаю, но я делаю некоторые тяжелые шаблоны, соответствующие этому классу case, поэтому я должен оставить его как класс case! – sparkr

+1

Возможно, вы хотите переименовать свой вопрос в smth как «Scala Case Class Multiple Parameter Lists» вместо Tupled –

ответ

13

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

Давайте проверим декларацию класса вместе с генерируемым apply и 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> 

Вы можете видеть, что хотя apply принимает 4 аргумента в общей сложности (кэрри), unapply отбрасывает второй список.

Давайте посмотрим, если мы можем получить доступ к членам второго списка:

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 
       ^

... Неа, а не обычным способом. Давайте проверим еще пару свойств:

scala> a.productArity 
res4: Int = 2 

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

Хорошо, похоже, что второй список аргументов в значительной степени игнорируется. Давайте копать:

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                     

Таким образом, нет никакой пользы c и d в конструкторе или в равных.

Вы все еще можете сделать второй список Arg Params полезно предваряя их 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  

Давайте посмотрим, как равенство и хэш-код работает в этом случае:

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 

, кажется, работает так, как вы хочу, чтобы это мне лично не нравилось;)

Для полноты, сопоставление по умолчанию по-прежнему так же, как и для класса A:

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

Спецификация языка Scala объясняет, как это работает here.

Формальные параметры в первом разделе параметров класса case называются элементами; они обрабатываются специально. Во-первых, значение такое значение может быть выделено как поле шаблона конструктора. Во-вторых, префикс val неявно добавляется к такому параметру, если только параметр содержит уже модификатор val или var. Следовательно, генерируется определение аксессора для параметра.

и

Каждый случай класса неявно отменяет некоторые определения метод класса scala.AnyRef если определение одного и того же метода не уже дано в себе или конкретное определение того же случая класса метод приведен в базовом классе класса case, отличном от AnyRef. В частности:

  • Метод равен: (Любой) Boolean является структурным равенство, где два экземпляра равны, если они оба принадлежат к классу дела в вопросе и они имеют равные (относительно равных) конструктор аргументы () ограничены элементами класса, то есть первым параметром , раздел).
  • Метод hashCode: Int вычисляет хэш-код. Если методы hashCode элементов структуры данных отображают равные (по отношению к равным) значениям равным хэш-кодам, то метод hashCode класса case также делает .
  • Метод toString: String возвращает строковое представление, которое содержит имя класса и его элементов.
+0

Очень хорошо объяснено! – sparkr

+1

Просто для полноты (учитывая, что в этом ответе не упоминается также «tupled»), способ получить запрограммированную функцию будет выглядеть примерно так: 'val tupled = (A.apply _). Tupled.andThen (_.tupled) '. Обратите внимание, что в качестве параметров требуется ** два ** кортежа (например, вы должны: tupled ((123, "foo")) (("bar", 456)) '). Преобразование этого в функцию, берущую только один кортеж (в вашем случае «Tuple4») не может быть достигнуто из коробки, не разрушая кортежи, поэтому в этот момент вы должны также написать метод пополнения вручную вручную. –

+2

В принципе: маска класса case применяется только к первому списку параметров. –

Смежные вопросы