java - 关于 Akka 和类型安全的一般问题

标签 java scala jvm akka type-erasure

问题 1:

JVM 不知道泛型,因此 Scala(和 Java)中的类型参数只存在于编译时。它们在运行时不存在。由于 Akka 是一个 Scala(和 Java)框架,它也有这个缺点。它尤其受到影响,因为在 Akka 中,参与者之间的消息(显然)仅在运行时交换,因此这些消息的所有类型参数都丢失了。到目前为止正确吗?

问题 2:

假设我定义了以下采用一个类型参数的案例类:

case class Event[T](t: T)

现在,我实例化一个 Event[Int](42) 并将其发送到我的 testActor。我的 testActor 基本上接收到一个 Event[Any] 并且不知道 t 是什么类型,这是否正确?

问题 3:

比如,在我的 testActor 中,存在一个也接受类型参数的函数:

def f[T](t: T) = println(t)

testActor 在接收到 Event 时调用 f:

override def receive: Receive = {
  case Event(t) => f(t)
}

这样调用函数时,f的类型参数T会被设置成什么? 任何?如果是这样,下面的函数是否有效地等同于上面的函数(假设它只会像上面描述的那样被调用):

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。所以类型信息毕竟没有被删除?我很困惑。

最佳答案

问题一

将类型删除称为“缺点”似乎有点像在回避问题,但不管怎样,这一段对我来说听起来很合理,可能对 list 和类标签以及“存在”的含义有一些质疑。 :)

问题2

不完全是。考虑以下类似的案例类和方法:

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
                                                ^

所以编译器知道一些成员的类型。

问题3

调用 f(t) 时推断出的 T 将是某种存在类型,因此不完全是 Any,但在此案例在道德上等同于它。但是,正如上面的 Foo 案例所示,如果 Event 具有涉及 T 的其他成员或方法,则编译器知道它是相同的 T

问题4

当我们说 JVM 删除类型时,我们实际上只是指“在通用上下文中”。每个对象(在 JVM 意义上)都有一个与之关联的类:

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 归因(我正在挥手比较类和类型,但你知道我的意思)。其次,由于泛型的类型删除,我们无法从 y.getClass 中获取有关元素类型的任何信息,而只能获取值的“顶级”类。

结论

在我看来,就类型删除而言,这是所有可能世界中最糟糕的一种。当然,您可以在 Scala 中在运行时分派(dispatch)类型!

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

然后:

scala> foo(List(true, false))
java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.Integer
  at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:101)
  at .foo(<console>:15)
  ... 31 elided

我个人更喜欢在运行时完全删除类型(至少在程序员可以看到的范围内)并且根本没有类型大小写匹配。或者我们可以使用 .NET 风格的具体泛型(在这种情况下我可能不会使用 Scala,但它仍然是一个合理且一致的选择)。事实上,我们有部分类型删除和损坏的类型大小写匹配。

关于java - 关于 Akka 和类型安全的一般问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41904658/

相关文章:

java - ExecutorCompletionService 未获取 Callable 返回的项目?

java - 调用 JNI 函数创建对象

java - Java中有 "void"可以参与的语句吗?

java - C++中new运算符和java中new运算符的区别

java - 尝试将 AddAll 添加到 MutableList 时出现不受支持的异常

Java 自动生成目录(如果存在)

scala - 构建 Scala 包依赖图

scala - Spark SQL + Cassandra : bad performance

scala - Akka 流 - 将 ByteString 流拆分为多个文件

performance - Scala 中的抽象类真的比特征表现更好吗?