将两个类型的通配符标识为相同

时间:2014-11-24 07:59:45

标签: scala types

假设我们将接口定义为:

trait Foo[A] {
  val value: A
  def perform(v: A): Unit
}

并编译此代码:

val n: Foo[_] = null
n.perform(n.value)

它看起来很完美......但是我们得到了一个神秘的错误:

error: type mismatch;
found   : n.value.type (with underlying type _$1)
required: _$1
n.perform(n.value)
            ^

那么......为什么会这样?我知道怎么解决这个问题;我只是好奇。谢谢!

2 个答案:

答案 0 :(得分:3)

您在此处未使用原始类型,您使用的是通配符类型。这类似于Java的

List<?> list = null;
list.add(list.get(0));

它不能编译,因为编译器分几步工作:

  1. 返回值n.value,编译器将该值标记为表示通配符。
  2. 执行方法n.perform,编译器只知道该参数是另一个通配符。
  3. 然而,两个通配符不一定相同。相反,发布了两个通配符。要进行此调用,您需要应用所谓的get-and-put principle。 get-put原则基本上意味着您临时命名通配符类型,以便Scala编译器可以推断两个通配符是相同的。这样做,以下代码编译:

    val n: Foo[_] = null
    def rebox[A](x: Foo[A]) = x.perform(x.value)
    rebox(n)
    

    并抛出NullPointerException

答案 1 :(得分:3)

让我们先看看Foo[X] forSome {type X}。这意味着:对于存在某些T的所有事物的类型,它们的类型为Foo [T]

注意T未显式映射到某种类型。所以它是一些未知的类型。

使用-Xprint:all进行编译会提供更多信息

jatinpuri@jatin:~/Desktop$ scalac -Xprint:all T.scala 
[[syntax trees at end of                    parser]] // T.scala
package <empty> {
    ...
    abstract trait Foo[A] extends scala.AnyRef {
      val value: A;
      def perform(v: A): Unit
    };
    val n: Foo[_$1] forSome { 
      <synthetic> type _$1
    } = null;
    n.perform(n.value)
  }
}

因此n.value会返回_$1类型。但是n.perform()也期望一个狂野类型。两种未知类型不能相同。进一步解释:

scala> trait Foo[A] {
     |   val value: A
     |   def perform(v: A): Unit
     | type i = A
     | }
defined trait Foo


scala> type I = Foo[X] forSome {type X}
defined type alias I

scala> val n : I = null;
n: I = null

scala> n.value:(I#i)
<console>:16: error: type mismatch;
 found   : n.value.type (with underlying type X)
 required: X
              n.value:(I#i)

我#i是X。但是n.value返回别的东西