多态类型中基本类型的类型信息

时间:2018-11-21 17:41:53

标签: scala polymorphism jvm-bytecode

给出以下对象:

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

当我们将文件编译为JVM字节码时,由于类型擦除以及Java不支持将原始类型用作泛型类型的参数的事实,该文件将转换为List<Object>

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

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编译器会将这种类型推断为{{1 }},而不是List[Int]

Type after taking a dependency on packaged JAR

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

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

1 个答案:

答案 0 :(得分:3)

JVM类文件格式允许编译器将自定义属性放入类文件中,请参见Section 4.7.1中的Java Virtual Machine Specification。除其他外,Scala编译器将有关名称的Scala签名的信息放入其生成的类文件中,以便在以后的编译器运行时,它可以再次读取此信息。 Java虚拟机必须忽略它们不了解的属性,因此这在运行时不会有所不同。

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

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

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

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