Общая типизация, так что результат одного метода может быть снова использован в качестве параметра другого метода
У меня есть код, который сводится к заводской инициализации объекта, а затем снова использовать этот объект для выполнения дополнительных операций:
trait Factory[T] {
def initialize(): T;
def finish(t: T): Unit;
}
Насколько я понимаю, результат initialize
всегда должен подходить для передачи finish
для всех Factory
экземпляр, независимо от T
,
Сам завод называется в точке, где неизвестно, что T
является:
object Minimal {
object StringFactory extends Factory[String] {}
val factories = Map[Int, Factory[_]](0 -> StringFactory)
val factory = factories(0)
// (1)
val obj = factory.initialize()
factory.finish(obj)
// (2)
def wrapper[T](factory: Factory[T]): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}
Хотя вариант (2) работает, вариант (1) не работает:
type mismatch; found : Minimal.obj.type (with underlying type Any) required: _$6
но я не могу понять, как это исправить. Это вообще возможно?
Что получает компилятор, вызывая wrapper
метод, который он не может понять сам? С моей точки зрения, obj
тип должен быть _$6
, так как компилятор, кажется, называет этот захват _
, Как я могу заставить компилятор понять это, не вводя совершенно новый метод для него?
3 ответа
Основываясь на ответе Режиса, я обнаружил, что компилятор выводит obj: Factory.T
, Оттуда это был маленький шаг, чтобы объединить это с предложением dk14 использовать type TT = T
, В результате получился общий и статически проверенный тип без использования метода-оболочки. Слава обоим!
Чтобы буквально ответить на оригинальный вопрос
С моей точки зрения, тип obj должен быть _$6, так как компилятор, кажется, называет этот захват _. Как я могу заставить компилятор понять это, не вводя совершенно новый метод для него?
Давая _$6
явное имя TT
, Конечно, методы должны также использовать это имя.
trait Factory[T] {
type TT = T
def initialize(): TT;
def finish(t: TT): Unit;
}
object Minimal {
object StringFactory extends Factory[String] {
def initialize(): TT = ""
def finish(t: TT): Unit = {}
}
val factories = Map[Int, Factory[_]](0 -> StringFactory)
val factory = factories(0)
// (1)
val obj: factory.TT = factory.initialize()
factory.finish(obj)
// (2)
def wrapper[T](factory: Factory[T]): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}
Экзистенциальный тип теряет свою экзистенциальность и становится верхним пределом после назначения его экземпляра самому val, поэтому любой способ без такого назначения будет работать, включая:
scala> trait Factory[T] { type TT = T; def initialize(): TT; def finish(t: TT): Unit;}
defined trait Factory
scala> val factory: Factory[_] = new Factory[Int] {def initialize = 5; def finish(t: Int) {}}
factory: Factory[_] = $anon$1@31d0ca61
scala> factory.finish(factory.initialize())
Это не будет работать:
scala> val obj = factory.initialize()
obj: Any = 5
scala> factory.finish(obj)
<console>:11: error: type mismatch;
found : Any
required: factory.TT
(which expands to) _$1
factory.finish(obj)
^
И это потому, что scala не будет видеть их типы одинаковыми (если только они не являются одним и тем же членом типа), поскольку экзистенциальность означает, что intialize()
может вернуть любой подкласс Any, когда finish()
может принять любой (но не всегда один) подкласс Any:
scala> trait Factory[T] { def initialize(): T; def finish(t: T): Unit;}
defined trait Factory
scala> val factory: Factory[_] = new Factory[Int] {def initialize = 5; def finish(t: Int) {}}
factory: Factory[_] = $anon$1@6e5da49
scala> factory.finish(factory.initialize())
<console>:10: error: type mismatch;
found : (some other)_$1(in value factory)
required: _$1(in value factory)
factory.finish(factory.initialize())
^
Таким образом, единственный способ связать ввод и вывод здесь - это разделение членов типа между ними.
Одним из решений было бы полностью заменить параметр типа абстрактным типом:
trait Factory {
type T
def initialize(): T;
def finish(t: T): Unit;
}
object Minimal {
object StringFactory extends Factory {
type T = String
def initialize(): T = ???
def finish(t: T): Unit = ???
}
val factories = Map[Int, Factory](0 -> StringFactory)
val factory: Factory = factories(0)
// (1)
val obj: factory.T = factory.initialize()
// Or simply (relying on inference): val obj = factory.initialize()
factory.finish(obj)
// (2)
def wrapper(factory: Factory): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}