Java:我如何确定初始化 block 中定义的本地类是否需要一个封闭实例来实例化?

标签 java class reflection static

现在我正在实现一个方法,该方法具有一个类型为 Class 的参数,如果给定的类对象需要它的封闭类的实例才能实例化,则此方法返回一个 boolean 值。

目前该方法的工作原理如下:

    if (clazz.getEnclosingClass() == null) {
        return false;
    }
    if (clazz.isAnonymousClass() || clazz.isMemberClass()) {
        return !Modifier.isStatic(clazz.getModifiers());
    }
    if (clazz.getEnclosingConstructor() != null) {
        return true;
    }
    final Method enclosingMethod = clazz.getEnclosingMethod();
    if (enclosingMethod != null) {
        return !Modifier.isStatic(enclosingMethod.getModifiers());
    }

解释一下为什么这样设计:

  1. 它首先检查它是否是顶级类,如果是,算法可以安全地返回 false
  2. 如果该类是匿名类或成员类,如果它不是静态的(如果在静态构造函数/方法/初始化程序 block 中声明的任意类自动是静态的,则它需要一个关闭实例)
  3. 现在可以将该类假定为本地类(忽略数组和原语),因此它是在构造函数、方法或初始化程序中定义的。但是,与匿名类不同的是,本地类永远不会被视为静态,但如果本地类是在非静态 block 中定义的,则仍然需要封闭实例。
  4. 构造函数永远不会是静态的,所以在那种情况下返回 true
  5. 如果它是在方法中定义的,如果该方法不是静态的,则返回 true

我需要第 6 步来确定本地类是驻留在静态初始化程序 block 还是实例初始化程序 block 中,因此我完成了此函数的实现。

所以这就是反射 API 的不足之处。反射包中没有 Class.getEnclosingInitializer() 等方法,也没有表示初始化程序的类。

初始化 block 不是类的成员吗?在 java 1.8 规范中,Member 接口(interface)只有实现类 Field、Executable(带有子类 Constructor 和 Method),然后还有 MemberName,它超出了大多数反射用户的范围。

我不太确定规范背后的人是否忘记了这种情况,如果在静态方法/初始化程序(如匿名类)中声明,本地类实际上应该是静态的。但在我看来,从这个角度来看,它缺乏最后一点一致性。

那么有人知道如何确定本地类是在哪种类型的初始化程序 block 中声明的吗?

我不太热衷于在字段中挖掘等于它的封闭类的合成类型,或者循环遍历它的构造函数以获得类似的东西(旁注:Constructor.getParameters() 中的参数对象 始终在 isImplicit()isSynthetic() 上返回 false,无论我尝试什么……这似乎是错误的)。所以,如果我能避免这样的解决方案,那就太好了。

最佳答案

一个有趣的谜题,但恐怕没有满足您要求的解决方案。一旦源文件被编译成字节码,关于类封闭范围的细节就丢失了。请记住,JVM 不仅适用于 Java 语言,而所描述的问题主要是特定于语言的。


I need step 6 to determine whether the local class resides in either a Static Initializer Block or an Instance Initializer Block

在运行时没有这样的信息可用。类文件具有本地或匿名类的 EnclosingMethod 属性 (JVMS §4.7.7)。但是,无法区分封闭实例初始化器和静态初始化器。规范明确指出

«如果当前类被实例初始化器、静态初始化器、实例变量初始化器或类变量初始化器立即包含在源代码中,method_index 必须为零»

考虑这两种情况:

class Outer {
    {
        // Local class inside instance initializer
        class Local {}
    }
}

对比

class Outer {
    static {
        // Local class inside static initializer
        class Local {
            final Outer this$0;

            Local(Outer outer) {
                this$0 = outer;
            }
        }
    }
}

它们被编译成几乎相同的类文件。唯一的区别是字段 this$0 在第一种情况下有 SYNTHETIC 标志,但在第二种情况下没有。但是检查这个标志正是你想要避免的。 (为什么?)


local classes actually should be static if declared in a static method/initializer (like anonymous classes)

我认为本地类和匿名类都不应该是静态的。此外,Java 语言规范要求它们是非静态的:

  • 修饰符 static 仅适用于成员类,不适用于顶级类、本地类或匿名类 ( JLS §8.1.1 )。
  • 匿名类始终是内部类;它永远不会是静态的 ( JLS §15.9.5 )。

因此,getModifiers() 有时会为匿名类返回 STATIC 显然违反了规范。有一个错误JDK-8034044这已在即将推出的 JDK 9 中修复。这会破坏算法的第 2 步。


好吧,那怎么办?这取决于你的实际意思

if the given class object requires an instance of it's enclosing class for it to be instantiated

我会说,上面的定义意味着一个类的所有构造函数都有一个封闭类类型的额外参数。在这种情况下,确实没有办法在没有封闭类实例的情况下实例化类。

但是,如果您真的想区分初始化器和静态初始化器,您唯一的机会(如我上面所示)是寻找带有 SYNTHETIC 标志的字段。

关于Java:我如何确定初始化 block 中定义的本地类是否需要一个封闭实例来实例化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35574537/

相关文章:

c++ - 为什么这个复制构造函数不起作用?

ios - 类似于关系数据库的模型的类或结构?

java 8反射不起作用

java - 函数调用中的对象扩展

java - listView结果检索并等待用户输入

java - 通过 slf4j 配置 log4j

java - 避免来自 Web 应用程序上下文根的版本号

c++ - 缺少类型说明符,编译器将 Class* 更改为 int*

C# 将 reflection.propertyinfo 转换为 Generic.List<>

c# - 获取 ICollection<T> 实现类的类型参数