How can I call the tupled method on this case class?
case class(a: Int, b: String)(c: String, d: Int)
The reason why I have my case class like this is because I want to use only the first two parameters to be considered for equals and hashCode comparison!
So how can I properly call the tupled on such a case class?
In short it does not seem like a good idea to use case classes this way. Here is an explanation.
Let's check the class declaration together with generated apply
and 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>
You can see that although apply
takes 4 arguments in total (curried), unapply
discards second list.
Let's see if we can access members of the second list:
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
^
... nope, not a regular way. Let's check couple more properties:
scala> a.productArity
res4: Int = 2
scala> a.productIterator.toList
res5: List[Any] = List(1, two)
Ok, it seems like second argument list is pretty much ignored. Let's dig 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
So there is no use of c
and d
in constructor or in equals.
You can still make second arg list params useful by prefixing them with 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
Let's see how equality and hashcode works in this case:
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
Seems to work the way you want it to, which I personally don't like ;)
For completeness, default pattern matching is still the way it was for class A
:
scala> B.unapply _
res15: B => Option[(Int, String)] = <function1>
Scala language spec explains how it works 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.
and
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With