scala 类型擦除 - 匹配大小写 class

scala type erasure - matching case class

我正在阅读有关类型擦除的内容,here

case class Thing[T](value: T)

def processThing(thing: Thing[_]) = {
  thing match {
    case Thing(value: Int) => "Thing of int"         //isn't thing Thing[_] anymore?
    case Thing(value: String) => "Thing of string"   //isn't thing Thing[_] anymore?
    case Thing(value: Seq[Int]) => "Thing of Seq[Int]"         //value=Seq[_] really
    case Thing(value: Seq[String]) => "Thing of Seq[String]"   //value=Seq[_] really
    case _ => "Thing of something else"
  }
}
println(processThing(Thing(Seq(1,2,3))))          //type erased, I get it
println(processThing(Thing(Seq("hello", "yo"))))  //type erased, I get it
println(processThing(Thing(1)))                   //why is this working?
println(processThing(Thing("hello")))             //why is this working?

我明白为什么 Seq[Int] 和 Seq[String] 没有被正确识别,在运行时两者都被视为 Seq[Object]。

但是,我不明白为什么前两个示例有效: 为什么 Thing[Int] 和 Thing[String] 都是 Thing[T] 没有与 Seq[T] 相同的问题...

为什么 Thing[Seq[T]] 被类型擦除 但是 Thing[T] (T=Int, String) 不是?

谁能解释一下这是怎么回事?谢谢

考虑以下 Java 代码:

class Foo {
  <T> void bar(T whatIsIt) {
    System.out.println("It is a: " + whatIsIt.getClass().getName());
  }

  public static void main() {
    Foo f = new Foo();
    f.bar(123);
    f.bar("Hello world");
    f.bar(f);
    f.bar(new ArrayList<String>());
  }
}

运行时只有一种方法Foo#bar(Object)。但是作为 whatIsIt 传入的对象仍然有一个指向任何 class 它 实际上 的指针。

Scala 可以访问与 Java 相同的元数据。因此,当它为 processThing 构建字节码时,它可以插入 value instanceOf Integer - 这就是它所做的:

scala> :javap processThing

// ... snip ...

  public java.lang.String processThing($line3.$read$$iw$Thing<?>);
    descriptor: (L$line3/$read$$iw$Thing;)Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=8, args_size=2
         0: aload_1
         1: astore_3
         2: aload_3
         3: ifnull        29
         6: aload_3
         7: invokevirtual #37                 // Method $line3/$read$$iw$Thing.value:()Ljava/lang/Object;
        10: astore        4
        12: aload         4
        14: instanceof    #39                 // class java/lang/Integer
        17: ifeq          26
        20: ldc           #41                 // String Thing of int

// ... snip ...

你是对的,在运行时 Thing(1)Thing("Hello") 将擦除到相同的 class Thing.

因此,如果您这样做:

thing match {
  case _: Thing[Int] => foo
  case _: Thing[String] => bar
}

您会看到预期的行为。

但是,您的模式匹配正在做一些不同的事情,它提取 thing 中的值,然后对其执行 class 检查。 值的class信息是自己保留的,所以你可以区分IntStringSeq,但看不到类型是什么Seq
的参数 但是,您可以尝试检查 Seq 的第一个元素......但这仍然不够,因为第一个元素可能是 Dog 而第二个元素可能是 Cat因为它是 Seq[Animal] 并且该检查比之前的检查更不安全,因为 Seq 可能是空的。