为什么内部 class 的子 class 需要封闭实例?
Why does a subclass of an inner class require an enclosing instance?
考虑 class OuterClass
其中有 InnerClass
public class OuterClass {
class InnerClass {
}
}
和第二个 class,它试图扩展 OuterClass
的 InnerClass
public class Clazz extends OuterClass.InnerClass {
public Clazz(OuterClass outerClass) {
outerClass.super();
}
}
到目前为止一切顺利,这段代码可以工作,编译器应该不会给出警告。
但我试图理解 - 为什么有必要将其传递给 OuterClass 的构造函数引用?为什么有必要调用它的超级构造函数?
我想了解为什么必须这样精确?
此外,考虑这个 class(与之前的无关),其中 classes Apartments
和 Hall
是 classes 到 Building
class 是 Solution
的内在(innerception)。
public class Solution {
public class Building {
public class Hall {
private BigDecimal square;
public Hall(BigDecimal square) {
this.square = square;
}
}
public class Apartments {
}
}
}
然后是顶层 class 试图扩展 inner inner class Hall
class BigHall extends Solution.Building.Hall {
public BigHall(Solution.Building building, BigDecimal square)
{
building.super(square);
}
}
看看这个烂摊子。最后两个 classes 应该也可以编译,但是你能帮我弄清楚吗,为什么 BigHall
class 扩展 inner inner class Hall
确实需要将构造函数引用传递给 Building 对象而不是 Solution 对象?
如果能提供JLS的报价就好了!
考虑以下因素
public class Outer {
String outerString;
class Inner {
public void doStuff() {
System.out.println("Outer string is " + outerString);
}
}
static class StaticInner {
public void doStuff() {
// can't access outerString here
}
}
}
在此示例中...每个 Inner
都需要一个 Outer
才能存在。 Inner
可以访问 Outer
中的所有字段
但是 StaticInner
不需要 Outer
就可以存在。它无法访问来自 Outer
的字段
为什么需要传递给 OuterClass 的构造函数引用?
仅仅是因为 InnerClass
的实例只能存在于 OuterClass
的实例中,并且可以直接访问其封闭实例的方法和字段。内(non-static)类are designed in Java就是这样。
UPD 当你要求 JLS 参考时,亲爱的 @Boris the Spider 在评论中提供了两次,它是 JLS 8.1.3。但是我个人觉得 JLS 对这个东西的描述相当混乱。官方 Java 文档教程以更简单的方式对这些内容进行了解释。
一个内部 class 可以在它的外部 class 之外被另一个 class 扩展。如果您要扩展静态内部 class(静态嵌套 class),那么它是一个直接的实现。如果要扩展 non-static inner class,则 sub class 构造函数必须使用 outer class 的实例显式调用 super class 构造函数。因为,如果没有 outer class.
的实例,您将无法访问 non-static inner class
示例代码
class OuterClass
{
class InnerClassTwo
{
//Class as a non-static member
}
}
//Extending non-static inner class or member inner class
class AnotherClassTwo extends OuterClass.InnerClassTwo
{
public AnotherClassTwo()
{
new OuterClass().super(); //accessing super class constructor through OuterClass instance
}
}
编译器生成的内部代码
class Outer$Inner
{
final Outer this[=11=];
Outer$Inner()
{ super();
this[=11=] = Outer.this;
}
}
您的 InnerClass
是 inner class。
An inner class is a nested class that is not explicitly or implicitly
declared static
.
内部 classes 可能有封闭实例
An instance i
of a direct inner class C
[your InnerClass
] of a
class or interface O
[your OuterClass
] is associated with an
instance of O
, known as the immediately enclosing instance of i
.
只有在静态上下文中声明的内部 classes 没有封闭实例。
An instance of an inner class I
whose declaration occurs in a static
context has no lexically enclosing instances.
您的示例的内部 class 不在静态上下文中,因此需要一个封闭实例。
Java Language Specification 然后说
The constructor of a non-private inner member class implicitly
declares, as the first formal parameter, a variable representing the
immediately enclosing instance of the class
(如果您有兴趣,它会进一步详细说明原因)。换句话说,您的 InnerClass
构造函数看起来确实像
public InnerClass(OuterClass OuterClass.this){} // this is valid syntax for entirely different reasons
它期望类型为 OuterClass
的参数用作其封闭实例。 Subclassing 这个 InnerClass
类型不会改变这一点,特别是因为任何子类型都必须调用其超类型的超级构造函数。
关于构造函数,specification also states
If a constructor body does not begin with an explicit constructor
invocation and the constructor being declared is not part of the
primordial class Object
, then the constructor body implicitly begins
with a superclass constructor invocation "super();", an invocation of
the constructor of its direct superclass that takes no arguments.
很明显,如果没有参数,您的代码将无法运行
public class Clazz extends OuterClass.InnerClass {
public Clazz() {
// implicit super() invocation
}
}
此 super()
构造函数调用无效。在这种情况下,这是因为语法错误。但是这个想法是,超级构造函数期望代表封闭实例的形式参数的参数,但您没有提供。正确的语法是您已有的语法。让我们定义它。
有multiple kinds of constructor invocations个。你的
public Clazz(OuterClass outerClass) {
outerClass.super();
}
是合格的超级class构造函数调用,defined如
Qualified superclass constructor invocations begin with a Primary
expression or an ExpressionName
. They allow a subclass constructor to
explicitly specify the newly created object's immediately enclosing
instance with respect to the direct superclass (§8.1.3). This may be
necessary when the superclass is an inner class.
chapter 解释了如何计算表达式
If the superclass constructor invocation is qualified, then the
Primary
expression or the ExpressionName
immediately preceding
".super
", p
, is evaluated.
[...]
Otherwise, the result of this evaluation is the immediately enclosing
instance of i
with respect to S
.
换句话说,outerClass
引用的对象成为您的 Clazz
实例所需的 封闭实例 。
简而言之,忽略内部 classes 的情况,您的示例可以归结为
public class Foo {}
public class Bar {
public Bar(Foo foo){}
}
public class SubBar extends Bar {
public SubBar(Foo foo) {
super(foo);
}
}
SubBar
必须满足需要 Foo
实例的 Bar
超级构造函数。这与您的 Clazz
类型发生的事情相同,只是它的超类型的构造函数是隐式的。
考虑 class OuterClass
其中有 InnerClass
public class OuterClass {
class InnerClass {
}
}
和第二个 class,它试图扩展 OuterClass
InnerClass
public class Clazz extends OuterClass.InnerClass {
public Clazz(OuterClass outerClass) {
outerClass.super();
}
}
到目前为止一切顺利,这段代码可以工作,编译器应该不会给出警告。
但我试图理解 - 为什么有必要将其传递给 OuterClass 的构造函数引用?为什么有必要调用它的超级构造函数?
我想了解为什么必须这样精确?
此外,考虑这个 class(与之前的无关),其中 classes Apartments
和 Hall
是 classes 到 Building
class 是 Solution
的内在(innerception)。
public class Solution {
public class Building {
public class Hall {
private BigDecimal square;
public Hall(BigDecimal square) {
this.square = square;
}
}
public class Apartments {
}
}
}
然后是顶层 class 试图扩展 inner inner class Hall
class BigHall extends Solution.Building.Hall {
public BigHall(Solution.Building building, BigDecimal square)
{
building.super(square);
}
}
看看这个烂摊子。最后两个 classes 应该也可以编译,但是你能帮我弄清楚吗,为什么 BigHall
class 扩展 inner inner class Hall
确实需要将构造函数引用传递给 Building 对象而不是 Solution 对象?
如果能提供JLS的报价就好了!
考虑以下因素
public class Outer {
String outerString;
class Inner {
public void doStuff() {
System.out.println("Outer string is " + outerString);
}
}
static class StaticInner {
public void doStuff() {
// can't access outerString here
}
}
}
在此示例中...每个 Inner
都需要一个 Outer
才能存在。 Inner
可以访问 Outer
但是 StaticInner
不需要 Outer
就可以存在。它无法访问来自 Outer
为什么需要传递给 OuterClass 的构造函数引用?
仅仅是因为 InnerClass
的实例只能存在于 OuterClass
的实例中,并且可以直接访问其封闭实例的方法和字段。内(non-static)类are designed in Java就是这样。
UPD 当你要求 JLS 参考时,亲爱的 @Boris the Spider 在评论中提供了两次,它是 JLS 8.1.3。但是我个人觉得 JLS 对这个东西的描述相当混乱。官方 Java 文档教程以更简单的方式对这些内容进行了解释。
一个内部 class 可以在它的外部 class 之外被另一个 class 扩展。如果您要扩展静态内部 class(静态嵌套 class),那么它是一个直接的实现。如果要扩展 non-static inner class,则 sub class 构造函数必须使用 outer class 的实例显式调用 super class 构造函数。因为,如果没有 outer class.
的实例,您将无法访问 non-static inner class示例代码
class OuterClass
{
class InnerClassTwo
{
//Class as a non-static member
}
}
//Extending non-static inner class or member inner class
class AnotherClassTwo extends OuterClass.InnerClassTwo
{
public AnotherClassTwo()
{
new OuterClass().super(); //accessing super class constructor through OuterClass instance
}
}
编译器生成的内部代码
class Outer$Inner
{
final Outer this[=11=];
Outer$Inner()
{ super();
this[=11=] = Outer.this;
}
}
您的 InnerClass
是 inner class。
An inner class is a nested class that is not explicitly or implicitly declared
static
.
内部 classes 可能有封闭实例
An instance
i
of a direct inner classC
[yourInnerClass
] of a class or interfaceO
[yourOuterClass
] is associated with an instance ofO
, known as the immediately enclosing instance ofi
.
只有在静态上下文中声明的内部 classes 没有封闭实例。
An instance of an inner class
I
whose declaration occurs in a static context has no lexically enclosing instances.
您的示例的内部 class 不在静态上下文中,因此需要一个封闭实例。
Java Language Specification 然后说
The constructor of a non-private inner member class implicitly declares, as the first formal parameter, a variable representing the immediately enclosing instance of the class
(如果您有兴趣,它会进一步详细说明原因)。换句话说,您的 InnerClass
构造函数看起来确实像
public InnerClass(OuterClass OuterClass.this){} // this is valid syntax for entirely different reasons
它期望类型为 OuterClass
的参数用作其封闭实例。 Subclassing 这个 InnerClass
类型不会改变这一点,特别是因为任何子类型都必须调用其超类型的超级构造函数。
关于构造函数,specification also states
If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class
Object
, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.
很明显,如果没有参数,您的代码将无法运行
public class Clazz extends OuterClass.InnerClass {
public Clazz() {
// implicit super() invocation
}
}
此 super()
构造函数调用无效。在这种情况下,这是因为语法错误。但是这个想法是,超级构造函数期望代表封闭实例的形式参数的参数,但您没有提供。正确的语法是您已有的语法。让我们定义它。
有multiple kinds of constructor invocations个。你的
public Clazz(OuterClass outerClass) {
outerClass.super();
}
是合格的超级class构造函数调用,defined如
Qualified superclass constructor invocations begin with a
Primary
expression or anExpressionName
. They allow a subclass constructor to explicitly specify the newly created object's immediately enclosing instance with respect to the direct superclass (§8.1.3). This may be necessary when the superclass is an inner class.
chapter 解释了如何计算表达式
If the superclass constructor invocation is qualified, then the
Primary
expression or theExpressionName
immediately preceding ".super
",p
, is evaluated.
[...]Otherwise, the result of this evaluation is the immediately enclosing instance of
i
with respect toS
.
换句话说,outerClass
引用的对象成为您的 Clazz
实例所需的 封闭实例 。
简而言之,忽略内部 classes 的情况,您的示例可以归结为
public class Foo {}
public class Bar {
public Bar(Foo foo){}
}
public class SubBar extends Bar {
public SubBar(Foo foo) {
super(foo);
}
}
SubBar
必须满足需要 Foo
实例的 Bar
超级构造函数。这与您的 Clazz
类型发生的事情相同,只是它的超类型的构造函数是隐式的。