Короче говоря, это не очень хорошая идея, чтобы использовать классы классов таким образом. Вот объяснение.
Давайте проверим декларацию класса вместе с генерируемым 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 возвращает строковое представление, которое содержит имя класса и его элементов.
Почему вы хотите, чтобы это был класс дел? Классы классов имеют некоторые удобные функции, но окупаемость для этого - это некоторая потеря гибкости. Просто определите свой собственный класс и переопределите equals/hashCode самостоятельно. –
Да, я понимаю, но я делаю некоторые тяжелые шаблоны, соответствующие этому классу case, поэтому я должен оставить его как класс case! – sparkr
Возможно, вы хотите переименовать свой вопрос в smth как «Scala Case Class Multiple Parameter Lists» вместо Tupled –