如何在此案例类中调用tupled方法?
case class(a: Int, b: String)(c: String, d: Int)
Run Code Online (Sandbox Code Playgroud)
我有这样的case类的原因是因为我想只使用前两个参数来考虑equals和hashCode比较!
那么如何在这样的案例类中正确调用tupled呢?
yǝs*_*ǝla 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>
Run Code Online (Sandbox Code Playgroud)
你可以看到虽然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
^
Run Code Online (Sandbox Code Playgroud)
......不,不是常规方式.让我们来看看更多的房产:
scala> a.productArity
res4: Int = 2
scala> a.productIterator.toList
res5: List[Any] = List(1, two)
Run Code Online (Sandbox Code Playgroud)
好吧,似乎第二个参数列表几乎被忽略了.让我们深入研究:
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
Run Code Online (Sandbox Code Playgroud)
所以没有使用c和d构造函数或等于.
您仍然可以通过为它们添加前缀来使第二个arg list 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
Run Code Online (Sandbox Code Playgroud)
让我们看看在这种情况下,相等和哈希码是如何工作的:
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
Run Code Online (Sandbox Code Playgroud)
似乎按照你想要的方式工作,我个人不喜欢;)
为了完整性,默认模式匹配仍然是类的方式A:
scala> B.unapply _
res15: B => Option[(Int, String)] = <function1>
Run Code Online (Sandbox Code Playgroud)
Scala语言规范解释了它在这里的工作原理.
案例类的第一个参数部分中的形式参数称为元素; 他们受到特殊待遇.首先,可以将这样的参数的值提取为构造函数模式的字段.其次,val前缀隐式添加到这样的参数中,除非该参数已经携带val或var修饰符.因此,生成参数的访问器定义.
和
每个case类隐式覆盖scala.AnyRef类的一些方法定义,除非在case类本身中已经给出了相同方法的定义,或者在不同于AnyRef的case类的某个基类中给出了相同方法的具体定义.特别是:
- 方法等于 :(任意)布尔值是结构相等,其中两个实例相等,如果它们都属于所讨论的案例类并且它们具有相等(相对于等于)构造函数参数(仅限于类的元素,即第一个参数)部分).
- 方法hashCode:Int计算哈希码.如果数据结构成员的hashCode方法映射相等(相对于equals)值为相等的哈希码,那么case类hashCode方法也是如此.
- 方法toString:String返回一个字符串表示形式,其中包含类及其元素的名称.