【问题标题】:Scala cast to generic typeScala 转换为泛型类型
【发布时间】:2020-06-16 15:44:07
【问题描述】:

我对泛型类型感到困惑。我希望2.asInstanceOf[A] 被转换为A 类型,同时它被转换为Int。 除此之外,输入是java.lang.Long,而输出是Int 的列表(根据定义,输入和输出应该是相同的类型)。这是为什么呢?

def whatever[A](x: A): List[A] = {
  val two = 2.asInstanceOf[A]
  val l = List(1.asInstanceOf[A],2.asInstanceOf[A])

  println(f"Input type inside the function for 15L: ${x.getClass}")
  println(f"The class of two: ${two.getClass}, the value of two: $two")
  println(f"The class of the first element of l: ${l.head.getClass}, first element value: ${l.head}")
  l
}

println(f"Returned from whatever function: ${whatever(15L)}")

输出:

Input type inside the function for 15L: class java.lang.Long
The class of two: class java.lang.Integer, the value of two: 2
The class of the first element of l: class java.lang.Integer, first element value: 1
Returned from whatever function: List(1, 2)

【问题讨论】:

    标签: scala casting type-erasure boxing


    【解决方案1】:

    a.asInstanceOf[B] 表示:

    亲爱的编译器;

    请忘记您认为a 的类型。我更了解。我知道如果a 不是实际上 输入B,那么我的程序可能会崩溃,但我真的很聪明,这不会发生。

    此致,超级程序员

    换句话说,val b:B = a.asInstanceOf[B] 不会创建 B 类型的新变量,它会创建一个新变量,就像它是 B 类型一样。如果a 的实际底层类型与B 类型兼容,那么一切都很好。如果a 的真实类型与B 不兼容,那么事情就会爆炸。

    【讨论】:

    • 这样的答案让我希望我有不止一票:)
    • 加油!寻找这样一个有趣的方法作为答案。
    【解决方案2】:

    Type erasure。出于类型检查的目的,2 被强制转换为 A;但在稍后的编译阶段A 被擦除为Object,所以你的代码变成了等价于

    def whatever(x: Object): List[Object] = {
      val two = 2.asInstanceOf[Object]
      val l = List(1.asInstanceOf[Object],2.asInstanceOf[Object])
    
      println(f"Input type inside the function for 15L: ${x.getClass}")
      println(f"The class of two: ${two.getClass}, the value of two: $two")
      println(f"The class of the first element of l: ${l.head.getClass}, first element value: ${l.head}")
      l
    }
    

    2.asInstanceOf[Object] 是一个 boxing operation,返回一个 java.lang.Integer

    如果您尝试实际使用返回值作为List[Long],您最终会得到ClassCastException,例如

    val list = whatever(15L)
    val x = list(0)
    

    x 将被推断为 Long 并插入一个演员表以解开预期的 java.lang.Long

    【讨论】:

      【解决方案3】:

      @jwvh 的回答恰到好处。在这里,我只会添加一个解决方案,以防您想解决将whatever 中的Int 安全转换为A 的问题,而不知道A 是什么。这当然只有在您提供一种从Int 构建特定A 的方法时才有可能。我们可以使用类型类来做到这一点:

      trait BuildableFromInt[+A] {
        def fromInt(i: Int): A
      }
      

      现在您只需为您希望在whatever 中使用的任何类型A 隐式提供BuildableFromInt

      object BuildableFromInt {
          implicit val longFromInt: BuildableFromInt[Long] = Long.box(_)
      }
      

      现在定义只接受兼容类型A

      def whatever[A : BuildableFromInt](x: A): List[A] = {
        val two = implicitly[BuildableFromInt[A]].fromInt(2)
        // Use two like any other "A"
        // ...
      }
      

      现在任何类型都可以使用BuildableFromInt 可用的任何类型。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-06-04
        • 1970-01-01
        • 1970-01-01
        • 2013-11-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多