给出以下对象:
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]
:
浏览List[Object]
文件后,我找不到有关类型参数的信息,该信息使Scala编译器可以成功地将其推断为.class
。
此元数据存储在哪里,以便我们可以将这种类型称为实际的List[Int]
而不是List[Int]
?
答案 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中的文件是一个好的开始。