函数对象内部 类 中的 variables/objects 会怎样?

What happens to variables/objects in inner classes of function objects?

我有一个函数 multi2,其中 returns 内部 class Inner 作为 Object

a 发生了什么 - 它保存在哪里,我如何访问它?

public class C {
    private static Object multi2(final int a) {
        class Inner {
            public int hashCode() {
                return 2*a;
            }
        }
        return new Inner();     // What happens to a?
                                // Who allocates a?
                                // Can I Access a?
    }

    public static void main(String[] args) {
        Object o = multi2(6);
        System.out.println("o.hashCode() = " + o.hashCode());

        o = multi2(4);
        System.out.println("o.hashCode() = " + o.hashCode());
    }
}

内部是所谓的本地classa 是传递给方法 multi2 的参数,可在该范围内访问。在该方法之外,您无法访问 a.

在实现级别发生的事情是 a 的值的副本保存在 合成实例变量 中,该变量在 [= 的编译版本中声明11=] class。

a 的值通过一个额外的参数传递给编译后的 Inner 构造函数。

C.Inner.hashCode方法使用合成变量的值。在Inner.hashCode的源代码中访问a,转化为在编译代码中访问相应的合成变量。

外部作用域中的变量必须是final1Innerclass中的合成变量必须是final2。这维持了 Inner class 的(可能)多个实例看到相同 a 变量的错觉。 (它们不是,但由于无法更改变量,因此内部 class 的代码无法区分。)

如果您使用 javap 查看编译示例的字节码,您将看到用于在外部和内部 classes 中实现它的机制。


1 - 或者从 Java 8 开始有效。

2 - 如果 a 可以通过 Inner 方法进行变异,那么两个具有相同外部 class 的 Inner 实例需要共享一个可变变量,其生命周期(现在)长于 multi2 调用的堆栈帧。这需要以某种方式将 a 从堆栈变量转换为堆上的东西。这将是昂贵和复杂的。

由于 "a" 是局部参数,您可以使用不同的方法来读取 "a" 值:

public class C {

  public static Object multi2(final int a) {
    return new Inner(a);
  }

  public static void main(String[] args) {
    Object o = multi2(6);
    System.out.println("o.hashCode() = " + o.hashCode());
    System.out.println("o.getA() = " + ((Inner) o).getA());

    o = multi2(4);
    System.out.println("o.hashCode() = " + o.hashCode());
    System.out.println("o.getA() = " + ((Inner) o).getA());
  }
}

class Inner{
  public int valueA;

  public Inner(int a)
  {
    valueA = a;
  }

  public int getA() {
    return valueA;
  }

  public int hashCode() {
    return 2*valueA;
  }
}

我想知道到底发生了什么,所以我编译了你的代码并查看了字节码输出。

基本上,编译器会向您的 class 'Inner' 添加一个构造函数。它还向采用 'a' 的构造函数添加了一个参数。如果您的 multi2() 方法不是静态的,那么可能还会有一个参数需要 'this',其中 'this' 是 multi2() 正在执行的 'C' 的实例。但是由于我们处于静态上下文中,因此没有 'this'.

编译器将一个私有最终字段添加到您的 class 'Inner' 并使用通过构造函数传递的值设置该私有字段。编译器还转换

    new Inner()

进入

    new Inner(a)

Hashcode 然后访问包含 a 值的私有字段。

如果'a'是一个对象而不是一个基本类型,那么它也是一样的,但是会传递一个引用而不是一个实际的数值。

你如何访问这个变量?好吧,你用反射访问它,但是有很多问题:

1) 你不知道编译器做出的字段名称,所以只能通过查看字节码来获取名称。不要相信反编译器,因为它们可能会更改名称。你得自己看看字节码才能知道。

2) 编译器可能将该字段标记为最终字段,这意味着即使您可以获得反射来为您访问该字段,您也无法更新它。

3) 字段名完全由编译器决定。字段名称可能会在构建之间发生变化,具体取决于编译器及其心情。

您已经在函数内部定义了 class Inner,因此 class 的范围将是 限制在方法中。并且您的 函数是静态的 因此只要加载 class 定义它就会存在。您已经覆盖了 InnerClass 中的 hashCode 函数,因此每次调用 multi2(param) 时,您都在为实例创建 hashCode InnerClass 并返回 InnerClass.

的实例

所以关于大家的问题,如有不妥请指正

a 会怎样? a 在您的静态方法的范围内,因此只要加载 class 定义,它就会存在。

谁分配一个? a 的范围被限制在静态方法内部,静态方法不需要实例来访问它,但至于静态 method/variable 分配,我认为这取决于 JVM。

我可以访问吗? 不,您不能从静态方法外部访问 a,它在您的静态方法中受到限制。