每个内部类都需要一个封闭实例是真的吗?
Posted
技术标签:
【中文标题】每个内部类都需要一个封闭实例是真的吗?【英文标题】:Is it true that every inner class requires an enclosing instance? 【发布时间】:2013-12-26 10:59:33 【问题描述】:术语内部类通常被认为是指“需要封闭实例的嵌套类”。但是,JLS 声明如下:
8.1.3. Inner Classes and Enclosing Instances
[...]
内部类包括本地(§14.3)、匿名(§15.9.5)和非静态成员类(§8.5)。
[...]
在静态上下文中声明的内部类的实例没有词法封闭实例。
还有,
15.9.5. Anonymous Class Declarations
[...]
匿名类始终是内部类(第 8.1.3 节);它永远不是
static
(第 8.1.1 节、第 8.5.1 节)。
众所周知,匿名类可以在静态上下文中声明:
class A
int t() return 1;
static A a = new A() int t() return 2; ;
用辛酸来形容,
new A()
是一个没有封闭实例的嵌套类,在静态上下文中定义,但它不是静态嵌套类——它是一个内部类。
在日常使用中,我们是否都给这些术语赋予了不恰当的含义?
作为一个相关的兴趣点,this historical specification document 将术语 top-level 定义为 inner 的对立面:
static
类成员的类和哪些类 是包成员都称为***类。它们不同于内 ***类只能直接使用它自己的实例 变量。
而在常见用法中,top-level 被视为与 nested 相反。
【问题讨论】:
看起来术语已经被不必要地混淆了...... 问题是,我们都依赖该术语进行交流 :) 随着时间的推移,我们似乎已经为自己制定了一个不同的术语,它更有用。 当然。这是我认为改变规范以匹配社区而不是相反的情况之一。 你最后的第 1 点和第 2 点是当你 删除static
而不是当你添加它时发生的。
@IanRoberts 谢谢,已更正(在答案中,我已经转移了该文本)。
【参考方案1】:
从规范的角度来看,问题中列出的区别非常合理:
内部类有限制,这与封闭实例的问题无关(例如,它可能没有静态成员);
静态嵌套类的概念基本上就是命名空间;这些类可能与我们通常认为的***类一起被称为***。
碰巧从嵌套类声明中删除 static
会同时做两件不同的事情:
-
它使类需要一个封闭的实例;
它使类内部。
我们很少将 inner 视为包含限制;我们只关注 enclosure instance 关注点,它更加明显。但是,从规范的角度来看,这些限制是一个至关重要的问题。
我们缺少的是需要封闭实例的类的术语。 JLS 没有定义这样的术语,所以我们(似乎不知道)劫持了一个相关但实际上本质上不同的术语来表示这个。
【讨论】:
通过删除单词static
,您只需为类添加一个隐式构造函数。为什么这么有意义?您始终可以显式创建完全相同的构造函数。
在相关的说明中,如果 Java 有一个特性可以让我不必编写 getter、setter、equals
、hashCode
、和toString
再一次——至少当我的预期实现是众所周知的默认值时。
对我来说静态和非静态比内部和嵌套更容易理解。他们对事物的定义更加清晰。使用内部/嵌套术语阅读 JLS 会很痛苦。
如果“非静态”的意思是“没有封闭的实例”,那么你的区别就不起作用了——如果这个问题就是这样:) 没有封闭实例的嵌套类,它仍然是非静态的!
“本地类”的概念再次与封闭实例无关。有些有,有些没有。在您的回答中,StaticInner
是没有封闭实例的嵌套类的示例。【参考方案2】:
好吧,在您的情况下,匿名类不也有封闭实例吗?它是静态的引用,而不是匿名类的实例。考虑:
class A
int t() return 1;
static A a = new A() System.out.println(t()); ;
【讨论】:
请注意this
(也称为第零个封闭实例)和我正在讨论的第一个封闭实例之间的区别,它简称为“封闭实例”。【参考方案3】:
静态内部类和无静态没有区别。我不明白为什么要单独考虑它们。看看下面的代码:
public class Outer
public static class StaticInner
final Outer parent;
public StaticInner(Outer parent)
this.parent = parent;
;
public class Inner
public static void main(String[] args)
new StaticInner(new Outer());
new Outer().new Inner();
然后在StaticInner
和Inner
类字节码:
public class so.Outer$Inner extends java.lang.Object
final so.Outer this$0;
public so.Outer$Inner(so.Outer);
Code:
0: aload_0
1: aload_1
2: putfield #1; //Field this$0:Lso/Outer;
5: aload_0
6: invokespecial #2; //Method java/lang/Object."<init>":()V
9: return
public class so.Outer$StaticInner extends java.lang.Object
final so.Outer parent;
public so.Outer$StaticInner(so.Outer);
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #2; //Field parent:Lso/Outer;
9: return
实际上它们之间根本没有区别。我想说非静态内部类只是语法糖。写普通东西的更短的方法,仅此而已。唯一的细微差别是,在非静态内部类中,对封闭类的引用是在调用父构造函数之前分配的,这可能会影响一些逻辑,但我认为这不是很关键,分开考虑。
附: One more question on a related topic, which might be interesting.
【讨论】:
你所说的StaticInner
根本不是内部类。它是嵌套的,但这与我的问题无关。是的,差异仅在源代码级别可见,在编译器强制执行的规则中。字节码是相同的,因为这是在 Java 1.1 中引入内部类时的设计要求。
具有封闭实例的类的句法后果一点也不小。如果你认为每个句法特征都是无关紧要的,那么所有图灵完备的语言在你眼中都是一样的。例如,Assembler 与 Haskell 处于同等地位。
基本上你是对的,但不要走到荒谬的地步。
当然,引入一个荒谬的从句只是帮助阐明我的观点的工具。我只是指出,如果从字面上看,这种特定的观点可以走多远。
@Mikhail 这并不荒谬,这是正确的术语。以上是关于每个内部类都需要一个封闭实例是真的吗?的主要内容,如果未能解决你的问题,请参考以下文章