函数对象内部类中的变量/对象会怎样?

时间:2018-07-02 11:24:33

标签: java inner-classes function-object

我有一个函数multi2,它将内部类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());
    }
}

5 个答案:

答案 0 :(得分:4)

在实现级别发生的事情是,将a的值的副本保存在C.Inner类的编译版本中声明的综合实例变量中。

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

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

外部作用域中的变量必须为final 1 。在final类中,合成变量必须为Inner 2 。这维持了这样的错觉:Inner类的多个实例(可能)正在看到相同的a变量。 (它们不是,但是由于不能更改变量,因此内部类的代码无法分辨出区别。)

如果使用javap查看已编译示例的字节码,则将在外部类和内部类中看到用于实现此目的的机制。


1-或从Java 8开始有效的最终版本。

2-如果a可以通过Inner方法进行突变,则具有相同外部类的两个Inner实例需要共享一个生存期为(现在)比multi2调用的堆栈帧长。这需要以某种方式将a从堆栈变量转换为驻留在堆中的内容。这将是昂贵和复杂的。

答案 1 :(得分:2)

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

关于您的问题,如果我错了,请纠正我。

一个会发生什么? a在您的静态方法范围内,因此只要加载了类定义,它就将处于活动状态。

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

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

答案 2 :(得分:0)

由于“ 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;
  }
}

答案 3 :(得分:0)

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

基本上,编译器会向您的“ Inner”类添加一个构造函数。它还向采用“ a”的构造函数添加单个参数。如果您的multi2()方法不是静态的,那么可能还会有一个参数采用“ this”,其中“ this”是multi2()在其上执行的“ C”的实例。但是,因为我们处于静态环境中,所以没有“ this”。

编译器将一个私有的final字段添加到类“ Inner”中,并使用通过构造函数传递的值来设置该私有字段。编译器还会转换

    new Inner()

进入

    new Inner(a)

然后,哈希码访问包含a值的私有字段。

如果“ a”是对象而不是原始对象,则将采用相同的方式,但是将通过引用而不是实际的数字值。

如何访问此变量?好吧,您可以通过反思来访问它,但是有很多问题:

1)您不知道编译器生成的字段的名称,因此只能通过查看字节码来获取名称。不要信任反编译器,因为它们可能会更改名称。您必须自己查看字节码才能发现。

2)编译器可能会将字段标记为最终字段,这意味着即使您可以通过反射来访问该字段,也无法对其进行更新。

3)完全由编译器来确定字段名称。字段名称可能会在版本之间更改,具体取决于编译器及其心情。

答案 4 :(得分:-1)

Inner是所谓的 local class a是传递给方法multi2的参数,可在该范围内访问。在该方法之外,您无法访问a