内部 classes 究竟如何访问外部 class 中的元素?
How exactly do inner classes access elements in an outer class?
我应该提一下,在我想问这个问题之后我已经弄清楚了具体的编程问题,所以这不是一个编程问题,而是一个关于问题背后原因的问题。
我一直在测试 Java 在使用访问修饰符时的局限性,并开始将这些测试应用于基本的继承概念。
代码如下:
package test.Inheritance;
public class SuperClass {
private static int x = 0;
protected static int y = 1;
public static void main(String[] args){
SupplementalClass2 child = new SupplementalClass2();
NestedClass local = new NestedClass();
InnerClass test;
child.setObject(child.new InnerClass(){
@Override public void display(){System.out.println("Hey!");}
});
test = child.getObject();
System.out.println(test.equals(child.receiveObject));
SuperClass.NestedClass.display();
SuperClass.NestedClass2.display();
test.display();
child.display();
local.message();
}
public static class NestedClass {
public static void display()
{
System.out.println("x before first static context change: " + x);
x = 25;
System.out.println("x after first static context change: " + x);
}
public void message()
{
System.out.print("Nested Class Field Access Test: " + "before(" + y + ") | ");
y = 20;
System.out.println("after(" + y + ")");
}
}
public static class NestedClass2 {
public static void display()
{
System.out.println("x before second static context change: " + x);
x = 30;
System.out.println("x after second static context change: " + x);
}
}
public class InnerClass {
public void display(){}
}
}
abstract class SupplementalClass extends SuperClass {
protected String test = "Parent Class String";
protected InnerClass receiveObject;
}
interface SupplementalInterface {
public static final int test = 3;
public abstract void display();
}
class SupplementalClass2 extends SupplementalClass implements SupplementalInterface {
public void display()
{
System.out.println("Supplemental Interface Field Access Test: " + SupplementalInterface.test);
System.out.println("Supplemental Parent Field Access Test: " + super.test);
}
public void setObject(InnerClass in){
receiveObject = in;
}
public InnerClass getObject()
{
return receiveObject;
}
}
这是固定版本:InnerClass
被赋予一个方法 display()
来覆盖 SupplementalClass2
中的方法。
之前,InnerClass
是空的,我试图在匿名 Class 实例中设置显示方法,而不是 class 本身,因为我相信内部 class会继承通过SupplementalInterface
实现的抽象显示方法。
所以我的问题是,如果不通过继承,嵌套和内部 classes 如何访问其持有者中的数据?
内部 class 实例访问其外部 class 实例的字段和方法就像任何对象访问另一个对象的字段和方法一样。唯一的区别是,为了能够访问私有成员,编译器生成由内部 class 调用的合成桥方法(非私有)以访问私有成员。
例如见class:
public class Outer {
private int privateField;
public int publicField;
private void privateFoo() {}
public void publicFoo() {}
private class Inner {
void bar() {
privateFoo();
publicFoo();
System.out.println("privateField = " + privateField);
System.out.println("publicField = " + publicField);
}
}
}
如果编译它并调用 javap -c Outer Outer.Inner
,您将得到以下输出:
Compiled from "Outer.java"
public class com.foo.Outer {
public int publicField;
public com.foo.Outer();
Code:
0: aload_0
1: invokespecial #3 // Method java/lang/Object."<init>":()V
4: return
public void publicFoo();
Code:
0: return
static void access[=11=]0(com.foo.Outer);
Code:
0: aload_0
1: invokespecial #2 // Method privateFoo:()V
4: return
static int access0(com.foo.Outer);
Code:
0: aload_0
1: getfield #1 // Field privateField:I
4: ireturn
}
Compiled from "Outer.java"
class com.foo.Outer$Inner {
final com.foo.Outer this[=11=];
void bar();
Code:
0: aload_0
1: getfield #1 // Field this[=11=]:Lcom/foo/Outer;
4: invokestatic #3 // Method com/foo/Outer.access[=11=]0:(Lcom/foo/Outer;)V
7: aload_0
8: getfield #1 // Field this[=11=]:Lcom/foo/Outer;
11: invokevirtual #4 // Method com/foo/Outer.publicFoo:()V
14: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
17: new #6 // class java/lang/StringBuilder
20: dup
21: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
24: ldc #8 // String privateField =
26: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: aload_0
30: getfield #1 // Field this[=11=]:Lcom/foo/Outer;
33: invokestatic #10 // Method com/foo/Outer.access0:(Lcom/foo/Outer;)I
36: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
39: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
42: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
48: new #6 // class java/lang/StringBuilder
51: dup
52: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
55: ldc #14 // String publicField =
57: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
60: aload_0
61: getfield #1 // Field this[=11=]:Lcom/foo/Outer;
64: getfield #15 // Field com/foo/Outer.publicField:I
67: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
70: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
73: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
76: return
}
如你所见,Outerclass有两个额外的静态方法:access[=13=]0()
和access0()
,分别调用私有方法和return的值私人领域。并且内部class通过这些方法调用私有方法并访问私有字段。
public 方法和字段以通常的方式访问,但是,因为没有什么可以阻止一个对象访问另一个对象的 public 成员。
我会让你在嵌套 classes 和静态成员上做同样的实验,看看它是如何精确工作的。
我应该提一下,在我想问这个问题之后我已经弄清楚了具体的编程问题,所以这不是一个编程问题,而是一个关于问题背后原因的问题。
我一直在测试 Java 在使用访问修饰符时的局限性,并开始将这些测试应用于基本的继承概念。
代码如下:
package test.Inheritance;
public class SuperClass {
private static int x = 0;
protected static int y = 1;
public static void main(String[] args){
SupplementalClass2 child = new SupplementalClass2();
NestedClass local = new NestedClass();
InnerClass test;
child.setObject(child.new InnerClass(){
@Override public void display(){System.out.println("Hey!");}
});
test = child.getObject();
System.out.println(test.equals(child.receiveObject));
SuperClass.NestedClass.display();
SuperClass.NestedClass2.display();
test.display();
child.display();
local.message();
}
public static class NestedClass {
public static void display()
{
System.out.println("x before first static context change: " + x);
x = 25;
System.out.println("x after first static context change: " + x);
}
public void message()
{
System.out.print("Nested Class Field Access Test: " + "before(" + y + ") | ");
y = 20;
System.out.println("after(" + y + ")");
}
}
public static class NestedClass2 {
public static void display()
{
System.out.println("x before second static context change: " + x);
x = 30;
System.out.println("x after second static context change: " + x);
}
}
public class InnerClass {
public void display(){}
}
}
abstract class SupplementalClass extends SuperClass {
protected String test = "Parent Class String";
protected InnerClass receiveObject;
}
interface SupplementalInterface {
public static final int test = 3;
public abstract void display();
}
class SupplementalClass2 extends SupplementalClass implements SupplementalInterface {
public void display()
{
System.out.println("Supplemental Interface Field Access Test: " + SupplementalInterface.test);
System.out.println("Supplemental Parent Field Access Test: " + super.test);
}
public void setObject(InnerClass in){
receiveObject = in;
}
public InnerClass getObject()
{
return receiveObject;
}
}
这是固定版本:InnerClass
被赋予一个方法 display()
来覆盖 SupplementalClass2
中的方法。
之前,InnerClass
是空的,我试图在匿名 Class 实例中设置显示方法,而不是 class 本身,因为我相信内部 class会继承通过SupplementalInterface
实现的抽象显示方法。
所以我的问题是,如果不通过继承,嵌套和内部 classes 如何访问其持有者中的数据?
内部 class 实例访问其外部 class 实例的字段和方法就像任何对象访问另一个对象的字段和方法一样。唯一的区别是,为了能够访问私有成员,编译器生成由内部 class 调用的合成桥方法(非私有)以访问私有成员。
例如见class:
public class Outer {
private int privateField;
public int publicField;
private void privateFoo() {}
public void publicFoo() {}
private class Inner {
void bar() {
privateFoo();
publicFoo();
System.out.println("privateField = " + privateField);
System.out.println("publicField = " + publicField);
}
}
}
如果编译它并调用 javap -c Outer Outer.Inner
,您将得到以下输出:
Compiled from "Outer.java"
public class com.foo.Outer {
public int publicField;
public com.foo.Outer();
Code:
0: aload_0
1: invokespecial #3 // Method java/lang/Object."<init>":()V
4: return
public void publicFoo();
Code:
0: return
static void access[=11=]0(com.foo.Outer);
Code:
0: aload_0
1: invokespecial #2 // Method privateFoo:()V
4: return
static int access0(com.foo.Outer);
Code:
0: aload_0
1: getfield #1 // Field privateField:I
4: ireturn
}
Compiled from "Outer.java"
class com.foo.Outer$Inner {
final com.foo.Outer this[=11=];
void bar();
Code:
0: aload_0
1: getfield #1 // Field this[=11=]:Lcom/foo/Outer;
4: invokestatic #3 // Method com/foo/Outer.access[=11=]0:(Lcom/foo/Outer;)V
7: aload_0
8: getfield #1 // Field this[=11=]:Lcom/foo/Outer;
11: invokevirtual #4 // Method com/foo/Outer.publicFoo:()V
14: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
17: new #6 // class java/lang/StringBuilder
20: dup
21: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
24: ldc #8 // String privateField =
26: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: aload_0
30: getfield #1 // Field this[=11=]:Lcom/foo/Outer;
33: invokestatic #10 // Method com/foo/Outer.access0:(Lcom/foo/Outer;)I
36: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
39: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
42: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
48: new #6 // class java/lang/StringBuilder
51: dup
52: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
55: ldc #14 // String publicField =
57: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
60: aload_0
61: getfield #1 // Field this[=11=]:Lcom/foo/Outer;
64: getfield #15 // Field com/foo/Outer.publicField:I
67: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
70: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
73: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
76: return
}
如你所见,Outerclass有两个额外的静态方法:access[=13=]0()
和access0()
,分别调用私有方法和return的值私人领域。并且内部class通过这些方法调用私有方法并访问私有字段。
public 方法和字段以通常的方式访问,但是,因为没有什么可以阻止一个对象访问另一个对象的 public 成员。
我会让你在嵌套 classes 和静态成员上做同样的实验,看看它是如何精确工作的。