gpt4 book ai didi

Scala无法解释的程序行为

转载 作者:行者123 更新时间:2023-12-04 08:21:04 29 4
gpt4 key购买 nike

对于以下代码:

object Test {

class MapOps(map: Map[String, Any]) {
def getValue[T](name: String): Option[T] = {
map.get(name).map{_.asInstanceOf[T]}
}
}

implicit def toMapOps(map: Map[String, Any]): MapOps = new MapOps(map)

def main(args: Array[String]): Unit = {

val m: Map[String, Any] = Map("1" -> 1, "2" -> "two")

val a = m.getValue[Int]("2").get.toString
println(s"1: $a")

val b = m.getValue[Int]("2").get
println(s"2: $b")
}
}
val a计算无一异常(exception),控制台打印 1: two ,
但是在计算时 val b , java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer被抛出。

此外,如果我执行
val c = m.getValue[Int]("2").get.getClass.toString
println(s"1: $c")

控制台打印“int”。

有人可以解释为什么这段代码表现得像这样吗?

最佳答案

这当然很奇怪。

如果您查看 Scala REPL 中的以下语句:

scala> val x = m.getValue[Int]("2")
x: Option[Int] = Some(two)

我认为正在发生的是: asInstanceOf[T]语句只是向编译器标记结果应该是 Int ,但不需要强制转换,因为对象仍然只是通过指针引用。 (并且 Int 值被装箱在 Option/ Some 内) .toString有效是因为每个对象都有一个 .toString方法,它只对值“二”进行操作以产生“二”。但是,当您尝试将结果分配给 Int 时变量,编译器尝试对存储的整数进行拆箱,结果是强制转换异常,因为值是 String而不是盒装 Int .

让我们在 REPL 中逐步验证这一点:
$ scala
Welcome to Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_151).
Type in expressions for evaluation. Or try :help.

scala> class MapOps(map: Map[String, Any]) {
| def getValue[T](name: String): Option[T] = {
| map.get(name).map{_.asInstanceOf[T]}
| }
| }
defined class MapOps

scala> import scala.language.implicitConversions
import scala.language.implicitConversions

scala> implicit def toMapOps(map: Map[String, Any]): MapOps = new MapOps(map)
toMapOps: (map: Map[String,Any])MapOps

scala> val a = m.getValue[Int]("2").get.toString
a: String = two

scala> println(s"1: $a")
1: two

到现在为止还挺好。请注意,到目前为止还没有抛出异常,即使我们已经使用了 .asInstanceOf[T]并使用 get在结果值上。重要的是,我们没有尝试对 get 的结果做任何事情。调用(名义上是一个盒装 Int,实际上是 String 值“二”)除了调用它的是 toString方法。那行得通,因为 String值有 toString方法。

现在让我们执行对 Int 的赋值。多变的:
scala> val b = m.getValue[Int]("2").get
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:101)
... 29 elided

现在我们得到了异常(exception)!还要注意导致它的堆栈跟踪中的函数: unboxToInt - 它显然试图转换存储在 Some 中的值到 Int它失败了,因为它不是盒装 Int但是一个 String.
问题的很大一部分是类型删除。不要忘记一个 Some(Banana)和一个 Some(Bicycle)是 - 在运行时 - 都只是 Some带有指向某个对象的指针的实例。 .asInstanceOf[T]无法验证类型,因为该信息已被删除。但是,编译器能够根据您告诉它的内容来跟踪类型应该是什么,但它只能在其假设被证明是错误的时检测到错误。

最后,关于 getClass调用结果。这有点编译器的花招。它实际上并不是在调用 getClass对象上的函数,但是 - 因为它认为它正在处理 Int ,这是一个原语 - 它只是替换一个 int类实例。
scala> m.getValue[Int]("2").get.getClass
res0: Class[Int] = int

验证对象实际上是 String ,您可以将其转换为 Any如下:
scala> m.getValue[Int]("2").get.asInstanceOf[Any].getClass
res1: Class[_] = class java.lang.String

进一步验证 get的返回值如下;请注意,当我们将此方法的结果分配给类型为 Any 的变量时没有异常。 (因此不需要强制转换),事实是有效的 Int key 为“1”的实际上存储在 Any 下作为盒装 Int ( java.lang.Integer ),并且后一个值可以成功地拆箱为常规 Int原始:
scala> val x: Any = m.getValue[Int]("2").get
x: Any = two

scala> x.getClass
res2: Class[_] = class java.lang.String

scala> val y: Any = m.getValue[Int]("1").get
y: Any = 1

scala> y.getClass
res3: Class[_] = class java.lang.Integer

scala> val z = m.getValue[Int]("1").get
z: Int = 1

scala> z.getClass
res4: Class[Int] = int

关于Scala无法解释的程序行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47020534/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com