问题1:
JVM不了解泛型,因此Scala(和Java)中的类型参数仅在编译时存在。它们在运行时不存在。由于Akka是一个Scala(和Java)框架,它也有这个缺点。特别是因为在Akka中,演员之间的消息(显然)只在运行时交换,所以这些消息的所有类型参数都丢失了。到目前为止正确吗?
问题2:
假设我定义了以下带有一个类型参数的case类:
case class Event[T](t: T)
现在,我实例化一个Event[Int](42)
并将其发送到我的testActor
。我的testActor
基本上收到Event[Any]
并且不知道t
是什么类型,这是否正确?
问题3:
说,在我的testActor
中,存在一个也带有类型参数的函数:
def f[T](t: T) = println(t)
testActor
在收到f
时调用Event
:
override def receive: Receive = {
case Event(t) => f(t)
}
当像这样调用函数时,T
的类型参数f
会被设置为什么? Any
?如果是这样,以下函数是否有效地与上述函数等效(假设它只会被调用,如上所述):
def f2(t: Any) = println(t)
问题4:
现在,请考虑f
:
def f[T](t: T) = println(t.getClass)
我没有改变呼叫网站:
override def receive: Receive = {
case Event(t) => f(t)
}
这不应该始终将Any
打印到控制台吗?当我向Event[Int](42)
发送testActor
时,它会将java.lang.Integer
打印到控制台。所以类型信息毕竟不会被删除?我很困惑。
答案 0 :(得分:17)
调用类型擦除"缺点"似乎有点像乞求这个问题,但无论如何,这一段对我来说听起来相当合理,可能有一些关于清单和类标签的狡辩以及什么"存在"手段。 :)
不完全是。考虑以下类似的案例类和方法:
case class Foo[T](v: T, f: T => Int)
def doSomething(x: Any): Unit = x match {
case Foo(v, f) => println(f(v))
case _ => println("whatever")
}
这很好用:
scala> doSomething(Foo("hello world", (_: String).size))
11
因此,我们不仅仅将Foo
视为Foo[Any]
,因为(_: String).size
不是有效的Any => Int
:
scala> val stringSize: Any => Int = (_: String).size
<console>:11: error: type mismatch;
found : String => Int
required: Any => Int
val stringSize: Any => Int = (_: String).size
^
因此,编译器知道关于成员类型的某些内容。
当您致电T
时推断的f(t)
将是某种存在类型,因此不完全是Any
,但在这种情况下,在道德上等同于它。正如上面的Foo
情况所示,如果Event
有其他成员或方法涉及T
,编译器将知道它是相同的{ {1}}。
当我们说JVM擦除类型时,我们只是在通用上下文中表示&#34;&#34;。每个对象(在JVM意义上)都有一个与之关联的类:
T
但是...
scala> val x: Any = "foo"
x: Any = foo
scala> x.getClass
res0: Class[_] = class java.lang.String
这里有两点需要注意。首先,我们得到的类值是一些更具体的子类型关系,如果我们不在scala> val y: Any = Seq(1, 2, 3)
y: Any = List(1, 2, 3)
scala> y.getClass
res1: Class[_] = class scala.collection.immutable.$colon$colon
归属的话,那么即使是推断类型也是如此(我有点挥手比较类和类型,但你知道我的意思)。其次,由于泛型的类型擦除,我们无法从: Any
获得有关元素类型的任何信息,只有&#34;顶级&#34;价值的等级。
在我看来,就类型擦除而言,这是所有可能世界中最糟糕的一种。当然你可以在Scala中在运行时调度类型!
y.getClass
然后:
def foo(x: Any): Unit = x match {
case s: String => println(s"I got a string: $s")
case d: Double => println("numbers suck!")
case xs: List[Int] => println(f"first int is ${ xs.head }%d")
case _ => println("something else")
}
但是:
scala> foo("bar")
I got a string: bar
scala> foo(List(1, 2, 3))
first int is 1
我个人更喜欢在运行时完全擦除类型(至少在程序员可以看到的情况下)并且根本不需要类型大小写匹配。或者我们可以使用.NET风格的reified通配(在这种情况下,我可能不会使用Scala,但它仍然是一个合理且一致的选项)。因为它是我们有部分类型擦除和破坏类型的案例匹配。