关于Java内部类

Java允许成员类(在其他类内定义的类),局部类(在语句块内定义的类)和匿名类(无名称的类):

class Outer {
    Object anonymous = new Object(){}; // this is an anonymous class
 
    // anonymous initialisation block
    {
        // this is a local class
        class Local{}
        Local l = new Local();
    }
 
    Outer() {
        // this is a local named class in a constructor
        class Local{}
        Local l = new Local();
    }
 
    void method() {
        // this is a local named class in a method
        class Local{}
        Local l = new Local();
    }
 
    // this is a member class
    class Inner{}
    Inner i = new Inner();
}

Java语言规范将成员,本地和匿名类分类为内部类 。

实施“细节”

Java语言或虚拟机规范没有告诉您的是它们是如何实现的。 其他文章已经对此进行了解释,例如Java编译器如何生成综合方法以允许这些成员类访问私有字段,而JVM不允许这样做。

易于理解的内部类的另一个实现细节是内部类的构造函数采用额外的综合参数。 相对众所周知 ,内部类构造函数的第一个综合参数将是其封闭实例,并将其存储在this$X综合字段中。 这对所有三种内部类均有效:成员,本地和匿名。

但是通常不知道捕获非恒定最终变量的本地类是否要求将所有这些变量作为额外的综合构造函数参数传递(捕获的常数最终变量将被内联并且不会生成额外的综合构造函数参数):

class Outer {
    void method() {
        final String constant = "foo";
        final String nonConstant = "foo".toUpperCase();
        class Local{
            /* synthetic fields and constructor:
 
            Outer this$0;
            String nonConstant;
 
            Local(Outer this$0, String nonConstant){
                this.this$0 = this$0;
                this.nonConstant = nonConstant;
            }
            */
        }
        Local l = new Local();
    }
}

好的,但是我为什么要关心呢?

在大多数情况下,除了您自己的好奇心之外,您根本不在乎。 但是,如果您正在使用内部类进行Java反射,那么您应该了解一些事情,并且由于我还没有在线找到或列出它们,所以我认为列出一系列可以帮助他人理解的事情很重要。因为不同的编译器将在Java Reflection API中产生不同的结果。

问题是当您使用Java反射为内部类构造函数获取java.lang.reflect.Constructor实例时会发生什么? 特别是,允许您访问参数类型(前泛型: getParameterTypes() ),通用参数类型(后泛型: getGenericParameterTypes() )和注释( getParameterAnnotations() )以及答案的方法会发生什么?是: 这取决于 。

假设以下Java类:

class Outer {
    class Inner {
        Inner(){}
        Inner(String param){}
        Inner(@Deprecated Integer param){}
    }
}

这是我们的每个构造函数上这三种反射方法返回的数组的大小,以及它们根据所使用的Java编译器的不同而有所不同:

外层内部类

.getDeclaredConstructor()

外层内部类

.getDeclaredConstructor(

String.class)

外层内部类

.getDeclaredConstructor(

Integer.class)

getParameterTypes()

。长度

1个

2

2

getGenericParameterTypes()

.length用Eclipse编译

1个

2

2

getGenericParameterTypes()

.length用Javac编译

0

1个

1个

getParameterAnnotations()

。长度

1个

2

1个

如您所见,合成参数始终包含在getParameterTypes() ,但仅在使用Eclipse进行编译时才包含在getGenericParameterTypes() 。

另一方面, getParameterAnnotations()将始终包含合成参数,除非注释了至少一个构造函数参数。

通过此信息,您现在了解了这些方法的结果之间的差异,但是到目前为止,我仍然没有找到确定哪个参数是合成参数的方法,因为尽管您可以对this$X合成做出很好的猜测参数,这是每个内部类都需要的,因此您无法知道将捕获为局部类构造函数的综合参数的非常量捕获变量的数量。

参考: Ceylon Team博客博客中的JCG合作伙伴 Stef Epardaud提供了带有内部类构造函数参数的Java反射奇数。

翻译自: https://www.javacodegeeks.com/2013/05/java-reflection-oddities-with-inner-class-constructor-parameters.html