3

我的代码归结为工厂初始化一个对象,然后再次使用该对象执行其他操作:

trait Factory[T] {
  def initialize(): T;

  def finish(t: T): Unit;
}

据我了解,initialize无论.finishFactoryT

工厂本身在不知道是什么的地方被调用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,因为编译器似乎将捕获的_. 我怎样才能让编译器意识到这一点,而不必为它引入一个全新的方法?

4

3 回答 3

3

存在类型在将其实例分配给 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())
                                               ^

所以在这里绑定输入和输出的唯一方法是在它们之间共享类型成员。

于 2015-01-15T09:28:48.600 回答
2

一种解决方案是用抽象类型完全替换类型参数:

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)
}
于 2015-01-15T09:37:52.093 回答
1

根据 Régis 的回答,我发现编译器推断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)
}
于 2015-01-15T10:03:40.033 回答