public class A {
public A() {
System.out.println("A()");
}
public class B {
public B() {
System.out.println("B()");
}
}
}
class Caller extends A.B {
Caller(A a){
a.super();
}
}
public class Main {
public static void main(String[] args) {
Caller as= new Caller(new A());
}
}
为什么我们需要在扩展内部类的类中调用a.super()
?它在做什么?
没有a.super()
程序不想编译。
Error:(48, 20) java: an enclosing instance that contains A.B is required
答案 0 :(得分:5)
a.super()
不会调用A
构造函数。由于A
中的new A()
表达式,Main.main()
构造函数会运行。
a.super()
调用nullary(和唯一)B
构造函数,指定a
作为对A
的包含实例的引用,该实例作为内部的子类班级A.B
,每个Caller
必须有。
答案 1 :(得分:3)
答案是:因为这是在Java语言规范中指定的方式。
您的班级A.B
是A
的内部班级。构造函数有一个类型为A
的隐藏参数 - 封闭(外部类)实例。
您的班级A.B
中已经包含Caller
,它本身不是内部类。但是超类的构造函数需要这个隐藏的A
实例 - 外部类实例。
在Java中传递此方法的方法是使用此a.super();
语法。
Java语言规范在section 8.8.7.1中定义了这个:
合格的超类构造函数调用以 Primary开头 表达式或 ExpressionName 。它们允许子类构造函数 显式指定新创建的对象立即封闭 关于直接超类的实例(第8.1.3节)。这可能是 当超类是内部类时必需。
答案 2 :(得分:0)
错误说明了一切。关于内部类(非静态嵌套的)的关键之一是它们可以访问其外部(封闭)实例。但内部类需要知道它属于哪个外部实例。此信息保存在我们无法访问的Outer this$0
引用中,但仍需将其设置为某个点,并且该点是构造函数的代码。
这就是我们通过outer.new Inner()
创建内部类实例的原因,如
Outer outerInstance= new Outer();
Outer.Inner innerInstance = outerInstance.new Outer.Inner();
// ^^^^^^^^^^^^^^^^^
由于这个原因,外部实例也被传递给Inner类构造函数(作为隐藏参数)。
既然你正在扩展内部类,这意味着你的类只会指定内部类,但这并不意味着它不再是 Inner
类型。
因此,由于Caller
的实例也被视为A.B
内部类(因为它扩展了它),您必须确保它将了解其外部实例(A
类)。为了使你在我们班的构造函数中成为可能,你需要
A.B
的构造函数,因此您可以让它在this$0
变量中保存该外部类引用。通过将A实例作为类构造函数Caller(A a)
的参数传递来解决第一点。
第二点与调用outerInstance.new Inner()
的方式类似,但这次我们不能使用new
关键字,因为我们不想创建Inner类的新对象。我们想调用超类构造函数中的代码来初始化当前对象(及其隐藏的this$0
字段)。通常的方法是致电super()
。只有可能有点奇怪的是,我们需要以某种方式让super()
调用哪个外部实例包含一个。所以可能使其与outer.new Inner()
语法类似,我们使用outer.super()
语法,在您的情况下是
a.super();
答案 3 :(得分:-1)
由于您的Caller
类扩展了A.B
,因此第一次调用Caller
构造函数将成为A.B
构造函数(即使您在代码中没有看到它) )。
因此a.super()
不会调用A.B
构造函数。
答案 4 :(得分:-2)
这不是a.super()
调用A.B
构造函数,而是Caller()
构造函数隐式调用它,因为Caller extends A.B
。只有在那之后才执行a.super()
行。