Class#getInterfaces() 和 Class#getGenericInterfaces() 返回不同长度的数组

bla*_*rag 5 java scala

如果您从 Scala 2.10.2 获取该类scala.runtime.AbstractPartialFunction(我没有检查其他版本)并比较 的输出AbstractPartialFunction.class.getInterfaces()AbstractPartialFunction.class.getGenericInterfaces()您可能会注意到,结果不匹配。通用接口是scala.Function1<T1, R>scala.PartialFunction<T1, R>,而 getInterfaces() 仅返回scala.PartialFunction。从scala文档中我可以看到通用信息是正确的,因为PartialFunction是一个Function1。

getInterfaces 的 javadoc 说:

如果此对象表示一个类,则返回值是一个数组,其中包含表示该类实现的所有接口的对象。数组中接口对象的顺序与该对象所表示的类的声明的implements 子句中接口名称的顺序相对应。

和 getGenericInterfaces 具有完全相同的文本。

由此(以及其他文本,包括 stackoverflow 信息)我得出结论,数组的顺序和长度是相等的。只是这里的情况并非如此。为什么?

到目前为止,我能够用几个 java7 和 java8 重现这个问题,没有尝试 java6 甚至 java5。

编辑:

AbstractPartialFunction 的 javap 输出(当然只有标头)是:

Compiled from "AbstractPartialFunction.scala"
public abstract class scala.runtime.AbstractPartialFunction<T1, R> implements scala.Function1<T1, R>, scala.PartialFunction<T1, R>`
Run Code Online (Sandbox Code Playgroud)

使用 asm lib 和 Textifier 在那里我可以看到这个头信息:

// class version 50.0 (50)
// access flags 0x421
// signature <T1:Ljava/lang/Object;R:Ljava/lang/Object;>Ljava/lang/Object;Lscala/Function1<TT1;TR;>;Lscala/PartialFunction<TT1;TR;>;
// declaration: scala/runtime/AbstractPartialFunction<T1, R> implements scala.Function1<T1, R>, scala.PartialFunction<T1, R>
public abstract class scala/runtime/AbstractPartialFunction implements scala/PartialFunction
Run Code Online (Sandbox Code Playgroud)

加上一个 ScalaSigAttribute。当然我没有展示这两种情况的方法

McD*_*ell 3

// Compiled from AbstractPartialFunction.scala (version 1.6 : 50.0, super bit)
// Signature: <T1:Ljava/lang/Object;R:Ljava/lang/Object;>Ljava/lang/Object;Lscala/Function1<TT1;TR;>;Lscala/PartialFunction<TT1;TR;>;
@scala.reflect.ScalaSignature(bytes="some bytes...")
public abstract class scala.runtime.AbstractPartialFunction implements scala.PartialFunction {
Run Code Online (Sandbox Code Playgroud)

类型的签名大致相当于以下 Java 声明:

class AbstractPartialFunction implements PartialFunction, Function1 {}
Run Code Online (Sandbox Code Playgroud)

PartialFunction扩展Function1

// Compiled from PartialFunction.scala (version 1.6 : 50.0, no super bit)
// Signature: <A:Ljava/lang/Object;B:Ljava/lang/Object;>Ljava/lang/Object;Lscala/Function1<TA;TB;>;
@scala.reflect.ScalaSignature(bytes="some bytes...")
public abstract interface scala.PartialFunction extends scala.Function1 {
Run Code Online (Sandbox Code Playgroud)

以下是Java 类文件的结构

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
Run Code Online (Sandbox Code Playgroud)

该类型实现的接口存储在常量池中,并通过接口数组中的引用记录:

签名作为属性存储属性数组中:

Signature属性记录任何类、接口、构造函数或成员的通用签名信息,这些类、接口、构造函数或成员在 Java 编程语言中的通用签名将包括对类型变量或参数化类型的引用。

所以这是我对正在发生的事情的有根据的猜测:

  • Class.getInterfaces()返回接口数组中的值
  • Class.getGenericInterfaces()使用类型签名构建其返回值
  • Scala 编译器从接口数组中删除Function1,因为PartialFunction扩展了Function1
  • Scala 编译器按原样保留类型签名

此行为与 Oracle JDK 从等效 Java 源生成的字节码不同,但它显然通过了 JVM 在加载类时进行的所有检查。