Общая типизация, так что результат одного метода может быть снова использован в качестве параметра другого метода

У меня есть код, который сводится к заводской инициализации объекта, а затем снова использовать этот объект для выполнения дополнительных операций:

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)
}
Другие вопросы по тегам