繁体   English   中英

关于阿卡和类型安全的一般问题

[英]General Questions about Akka and Typesafety

问题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在收到Event时调用f

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

当像这样调用函数时, f的类型参数T会被设置为什么? 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打印到控制台。 那么类型信息毕竟不会被删除? 我很困惑。

问题1

调用类型擦除“缺点”似乎有点像乞求问题,但无论如何,这一段对我来说听起来相当合理,可能有一些关于清单和类标签以及“存在”意味着什么的狡辩。 :)

问题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中在运行时调度类型!

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风格的reified泛型(在这种情况下,我可能不会使用Scala,但仍然是一个合理且一致的选项)。 因为它是我们有部分类型擦除和破碎类型的案例匹配。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM