多态类型中原始类型的类型信息

Type information for primitive types in polymorphic types

给定以下对象:

object Foo {
  val bar: List[Int] = List(1, 2, 3)
}

当我们将此文件编译为 JVM 字节码时,由于类型擦除以及 Java 不支持原始类型作为泛型类型的参数这一事实,这被翻译成 List<Object> .

我们可以通过使用 javap -l 编译和检查 .class 来看到这一点:

public static com.yuvalitzchakov.github.Foo$ MODULE$;
  descriptor: Lcom/yuvalitzchakov/github/Foo$;
  flags: ACC_PUBLIC, ACC_STATIC

public scala.collection.immutable.List<java.lang.Object> bar();
  descriptor: ()Lscala/collection/immutable/List;
  flags: ACC_PUBLIC
  Code:
    stack=1, locals=1, args_size=1
        0: aload_0
        1: getfield      #19                 // Field bar:Lscala/collection/immutable/List;
        4: areturn
      LineNumberTable:
        line 4: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/yuvalitzchakov/github/Foo$;
    Signature: #17                          // ()Lscala/collection/immutable/List<Ljava/lang/Object;>;

但是,如果我们将其编译成 JAR 文件,然后将其作为不同 Scala 项目中的依赖项并尝试将 Foo.bar 设置为不同的值,Scala 编译器会将此类型推断为 List[Int],不是 List[Object]:

浏览完 .class 文件后,我找不到有关类型参数的信息,而这将使 Scala 编译器成功地将其推断为 List[Int]

此元数据存储在哪里,以便我们可以将此类型称为实际 List[Int] 而不是 List[Object]

JVM class 文件格式允许编译器将自定义属性放入 class 文件中,请参阅 Section 4.7.1 of the Java Virtual Machine Specification。除其他事项外,Scala 编译器将有关名称的 Scala 签名的信息放入它生成的 class 文件中,以便在以后的编译器 运行 上,它可以再次读取此信息。 Java 虚拟机需要忽略它们不理解的属性,因此这在 运行 时没有影响。

我没有找到注释的二进制格式规范,但如果您想深入了解实现,我发现:

对于 Scala 3.0,甚至计划使用新的 "tasty" 格式在 class 文件中存储完整的抽象语法树,包括类型检查器生成的信息。美味代表 "typed abstract syntax trees"。基本思想是在类型检查阶段之后序列化抽象语法树并将其放入 class 文件中。以后的编译器 运行s 可以加载依赖项的完整抽象语法。这不仅可以进行类型检查,还可以进行跨模块内联和其他全局优化。

Tasty 计划成为 Scala 抽象语法树的通用交换格式,也用于编译器和集成开发环境之间的通信以及元编程。

如果您想深入了解实现,也许 https://github.com/lampepfl/dotty/tree/master/compiler/src/dotty/tools/dotc/core/tasty 中的文件是一个好的开始。