Scala Case Class 元组

Scala Case Class Tupled

如何在这种情况下调用元组方法class?

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

之所以有我的案例class是因为我只想使用前两个参数来进行equals和hashCode比较!

那么在这种情况下如何正确调用元组 class?

简而言之,以这种方式使用 case classes 似乎不是一个好主意。这里有一个解释。

让我们检查 class 声明以及生成的 applyunapply:

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 个参数(curried),但是 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                                                                                  

因此在构造函数或等式中没有使用 cd

您仍然可以使第二个 arg 列表参数有用,方法是在它们前面加上前缀 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

似乎按照你想要的方式工作,我个人不喜欢;)

为了完整起见,默认模式匹配仍然是 class A:

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

Scala 语言规范解释了它是如何工作的here

The formal parameters in the first parameter section of a case class are called elements; they are treated specially. First, the value of such a parameter can be extracted as a field of a constructor pattern. Second, a val prefix is implicitly added to such a parameter, unless the parameter carries already a val or var modifier. Hence, an accessor definition for the parameter is generated.

Every case class implicitly overrides some method definitions of class scala.AnyRef unless a definition of the same method is already given in the case class itself or a concrete definition of the same method is given in some base class of the case class different from AnyRef. In particular:

  • Method equals: (Any)Boolean is structural equality, where two instances are equal if they both belong to the case class in question and they have equal (with respect to equals) constructor arguments (restricted to the class's elements, i.e., the first parameter section).
  • Method hashCode: Int computes a hash-code. If the hashCode methods of the data structure members map equal (with respect to equals) values to equal hash-codes, then the case class hashCode method does too.
  • Method toString: String returns a string representation which contains the name of the class and its elements.