为什么类型字段的上限在强制转换后丢失(参见说明中的示例)?

Joh*_*ood 3 types scala

定义以下内容后:

abstract class A {
  type T
  def print(p: T) = println(p.toString)
}

trait B extends A {
  type T <: String
}
Run Code Online (Sandbox Code Playgroud)

正如所料,我们无法创建一个对象T = Int:

scala> val a = new A with B {type T = Int}
<console>:9: error: overriding type T in trait B with bounds >: Nothing <: String;
 type T has incompatible type
       val a = new A with B {type T = Int}
                                  ^
Run Code Online (Sandbox Code Playgroud)

正如所料,我们可以创建一个对象T = String:

scala> val a = new A with B {type T = String}
a: A with B{type T = String} = $anon$1@692dec

scala> a.print("test")
test
Run Code Online (Sandbox Code Playgroud)

将我们的值a转换为类型后A with B,调用print方法时会出错.似乎类型字段T丢失了有关类型(?)的信息.

scala> val b = a.asInstanceOf[A with B]
b: A with B = $anon$1@1927275

scala> b.print("test")
<console>:15: error: type mismatch;
 found   : java.lang.String("test")
 required: b.T
              b.print("test")
                      ^
Run Code Online (Sandbox Code Playgroud)

问题1:为什么T演员表后关于类型字段的信息会丢失?

好的,所以我们再次尝试使用强制转换,将类型字段显式设置TString类型:

scala> val c = a.asInstanceOf[A with B {type T = String}]
c: A with B{type T = String} = $anon$1@1927275

scala> c.print("test")
test
Run Code Online (Sandbox Code Playgroud)

好的,这很有效 - 很好.

现在让我们尝试疯狂的事情:

scala> val d = a.asInstanceOf[A with B {type T = Int}]
d: A with T{type T = Int} = $anon$1@1927275

scala> d.print(3)
3
Run Code Online (Sandbox Code Playgroud)

问题2:嗯?Trait B限制类型T是String的子类型,但现在该print方法适用于整数.这为什么有效?

0__*_*0__ 5

问题1 - "将我们的值a转换为带有B的类型A后,我们在调用print方法时出错." T演员之后有什么信息?正是这样B:

type T <: String

因此,类型是未知的,只是它的上限.以下显示禁止print呼叫的原因A with B:

trait X
trait Y extends X { def hallo() = () }

trait A {
  type T
  def test(t: T) = ()
}

trait B extends A {
  type T <: X
}

val y = new A with B { type T = Y; override def test(t: T): Unit = t.hallo() }
y.test(new X {})     // refused -- rightfully
y.test(new Y {})     // ok

val yc = y: A with B // note how we can cast type safe this way!
yc.test(new X {})    // refused -- rightfully (would try to call inexistent 'hallo')
Run Code Online (Sandbox Code Playgroud)

所以这是在逆变(方法论证)位置发生的类型会发生什么问题.如果你B通过缩小下限来定义,即即使没有修复type T >: X也可以调用.testT


问题2 - 当然有效.您可以使编译器允许使用类型转换进行任何调用.在转换为之后A with B {type T = Int},强制编译器接受它T = Int.现在,toString您调用的方法是定义的java.lang.Object,并且由于通用结构A,您将Int被装入a中java.lang.Integer,因此在调用时您不会看到任何运行时问题toString.

但是认为你在这里做正确的事情是错误的.例如:

abstract class A {
  type T
  def print(p: T) = println(p.toString)
}

trait B extends A {
  type T <: String
  override def print(p: T) = println(p.toUpperCase) // !
}

val a = new A with B { type T = String }
val b = a.asInstanceOf[A with B { type T = Int }]
b.print(33)  // bang -- runtime class cast exception
Run Code Online (Sandbox Code Playgroud)