Рассмотрим абстрактный класс, определяющий два свойства
abstract class A {
def a: Int
def b: Int
// real A has additional members
}
который является базовым классом для различных классов случаев, таких как
case class Foo(a: Int, b: Int) extends A
case class Bar(a: Int, b: Int) extends A
// and many more
Цель: наконец-то я хотел бы иметь возможность создавать экземпляры вышеупомянутых классов case двумя способами, а именно
val b1 = Bar(1, 2)
val b2 = Bar(1) has 2
assert(b1 == b2) // must hold
Подход: поэтому кажется разумным определить вспомогательный класс, который определяет has
и позволяет мне частично создавать A
s
case class PartialA(f: Int => A) {
def has(b: Int) = f(b)
}
Проблема. Существующий механизм не поддерживает такие вызовы, как Bar(1)
, поскольку на самом деле это вызов Bar.apply(1)
, то есть метода apply
, как определено сгенерированным компилятором объектом Bar
.
Было бы здорово, если бы я мог заставить компилятор генерировать объект Bar
как object Bar extends PartialAConstructor
, где
abstract class PartialAConstructor{
def apply(a: Int, b: Int): A // abstract, created when the compiler creates
// object Bar
def apply(a: Int) = PartialA((b: Int) => apply(a, b))
}
Однако не представляется возможным повлиять на создание сопутствующих объектов классов case.
Желаемые свойства:
Классы case:
Foo
,Bar
и т. д. должны оставаться классами case, потому что я хотел бы использовать сгенерированные компилятором вкусности, такие как структурное равенство,copy
и автоматически генерируемые экстракторы.«Полное» структурное равенство: определение классов прецедентов как
case class Bar(a: Int)(val b: Int)
не является вариантом, потому что метод
equals
, сгенерированный компилятором, рассматривает только первый список аргументов, и, следовательно, следующее будет ошибочным:assert(Foo(1)(0) == Foo(1)(10))
Как можно меньше повторений кода: например, конечно, можно определить
def Bar(a: Int) = PartialA((b: Int) => Bar(a, b))
но это нужно было бы сделать для каждого класса case, расширяющего
A
, то естьFoo
,Bar
и т. д.