为什么我们不能在(非静态)内部类中使用静态方法?

为什么我们不能在非静态的内部类中使用静态方法?

如果我让内部类静态它的作品。 为什么?

由于内部类的一个实例隐含地与其外部类的一个实例相关联,因此它本身不能定义任何静态方法。 由于静态嵌套类不能直接引用其封闭类中定义的实例variables或方法,因此只能通过对象引用来使用它们,因此在静态嵌套类中声明静态方法是安全的。

在非静态内部类中允许使用静态方法没有多less意义; 你将如何访问它? 你不能访问(至less在最初)一个非静态的内部类实例,而不经过一个外部类实例。 没有纯静态的方法来创build一个非静态的内部类。

对于外部类Outer ,可以像这样访问静态方法test()

 Outer.test(); 

对于静态内部类Inner ,可以像这样访问其静态方法innerTest()

 Outer.Inner.innerTest(); 

但是,如果Inner不是静态的,那么现在就没有纯静态的方法来引用方法innertest 。 非静态内部类与他们外部类的特定实例绑定在一起。 函数与常量不同,因为对Outer.Inner.CONSTANT的引用保证是明确的,函数调用Outer.Inner.staticFunction(); 不是。 假设你有Inner.staticFunction() ,它调用在Outer定义的getState() 。 如果您尝试调用该静态函数,则您现在对Inner类有一个模棱两可的引用。 也就是说,你调用静态函数的内部类的哪个实例? 这很重要。 看到,由于对外部对象的隐式引用,没有真正的静态方法来引用该静态方法。

Paul Bellora是正确的,语言devise师可以允许这样做。 然后他们必须小心地禁止在非静态内部类的静态方法中访问对外部类的隐式引用。 在这一点上,如果你不能引用外部类,除了静态外,这是一个内部类的价值是什么? 如果静态访问是好的,那么为什么不声明整个内部类是静态的呢? 如果你简单地使内部类本身是静态的,那么你就没有对外部类的隐式引用,并且你不再有这种模糊性。

如果你真的需要静态方法在一个非静态的内部类,那么你可能需要重新考虑你的devise。

我有一个理论,可能会也可能不会是正确的。

首先,你应该知道一些关于如何在Java中实现内部类的事情。 假设你有这个类:

 class Outer { private int foo = 0; class Inner implements Runnable { public void run(){ foo++; } } public Runnable newFooIncrementer(){ return new Inner(); } } 

当你编译它时,生成的字节码看起来就像你写了这样的东西:

 class Outer { private int foo = 0; static class Inner implements Runnable { private final Outer this$0; public Inner(Outer outer){ this$0 = outer; } public void run(){ this$0.foo++; } } public Runnable newFooIncrementer(){ return new Inner(this); } } 

现在,如果我们在非静态内部类中允许使用静态方法,那么可能需要这样做。

 class Outer { private int foo = 0; class Inner { public static void incrFoo(){ foo++; } } } 

……看起来相当合理,因为Inner类似乎每个Outer实例都有一个化身。 但正如我们上面看到的,非静态内部类实际上只是静态“内部”类的语法糖,所以最后一个例子大致等价于:

 class Outer { private int foo = 0; static class Inner { private final Outer this$0; public Inner(Outer outer){ this$0 = outer; } public static void incrFoo(){ this$0.foo++; } } } 

…这显然是行不通的,因为this$0是非静态的。 这就解释了为什么不允许使用静态方法(虽然只要不引用封闭对象,就可以使静态方法成为可能),以及为什么不能使用非最终静态字段(如果来自不同对象的非静态内部类的实例共享“静态”,那将是违反直觉的)。 这也解释了为什么final字段允许的(只要不引用封闭对象)。

唯一的原因是“不一定”,那为什么还要支持呢?

在语法上,没有理由禁止内部类具有静态成员。 虽然Inner一个实例与Outer一个实例相关联,但是如果java决定这样做的话,仍然可以使用Outer.Inner.myStatic来引用一个Inner的静态成员。

如果你需要在Inner所有实例中共享一些东西,你可以把它们放到Outer作为静态成员。 这并不比在Inner使用静态成员更差, Outer仍然可以访问内部的任何私有成员(不会改进封装)。

如果你需要在由一个outer对象创build的Inner所有实例之间共享某个东西,那么把它们作为普通成员放到Outer类中更有意义。

我不同意“静态嵌套类几乎只是一个顶级类”。 我认为最好把静态嵌套类/内部类作为外部类的一部分,因为它们可以访问外部类的私有成员。 而外部阶层的成员也是“内部阶层的成员”。 所以没有必要在内部支持静态成员。 外部类中的普通/静态成员就足够了。

简短的回答:大多数程序员关于范围如何工作的思维模型不是javac使用的模型。 匹配更直观的模型将需要对javac的工作方式进行大的改变。

内部类中的静态成员是可取的主要原因是代码清洁 – 只有内部类才使用的静态成员应该在内部生活,而不是放在外部类中。 考虑:

 class Outer { int outID; class Inner { static int nextID; int id = nextID++; String getID() { return outID + ":" + id; } } } 

考虑当我使用非限定标识符“outID”时,getID()中发生了什么。 这个标识符出现的范围如下所示:

 Outer -> Inner -> getID() 

在这里,再次因为这只是javac的工作方式,范围的“Outer”级别包括Outer的静态和实例成员。 这是令人困惑的,因为我们通常被告知将类的静态部分看作是范围的另一个层次:

 Outer static -> Outer instance -> instanceMethod() \----> staticMethod() 

在这种思考方式中,当然staticMethod()只能看到Outer的静态成员。 但是如果这是javac工作方式,那么在静态方法中引用实例variables将导致“名称无法parsing”错误。 真正发生的事情是这个名字是在范围内find的,但是后来又有一个额外的检查,并且发现这个名字是在一个实例上下文中声明的,并且是从静态上下文中引用的。

好的,这与内部类有什么关系? 天真地说,我们认为没有理由内部类不能有一个静态范围,因为我们正在像这样描绘范围:

 Outer static -> Outer instance -> Inner instance -> getID() \------ Inner static ------^ 

换句话说,内部类中的静态声明和外部类中的实例声明都在内部类的实例上下文范围内,但是这两者中的任何一个实际上都嵌套在另一个中; 两者都嵌套在Outer的静态范围内。

这不仅仅是javac的工作原理 – 静态和实例成员都有一个单一级别的范围,范围总是严格的嵌套。 即使inheritance是通过将声明复制到子类中而不是分支和search超类作用域来实现的。

为了支持内部类的静态成员,javac将不得不拆分静态和实例作用域, 支持分支和重新连接作用域的层次结构,否则将不得不扩展其简单的布尔“静态上下文”思想,以便在各级跟踪上下文的types在当前范围内的嵌套类。

为什么我们不能在非静态的内部类中使用静态方法?

注意:一个非静态的嵌套类被称为内部类,所以你没有non-static inner class

没有相应的外部类实例,内部类实例就不存在了。 内部类不能声明编译时间常量以外的静态成员。 如果允许的话,那么static意义就不明确了。 在这种情况下,会出现一些混乱:

  1. 这是否意味着VM中只有一个实例?
  2. 或者每个外部对象只有一个实例?

这就是为什么devise师可能根本就没有处理这个问题的决定。

如果我让内部类静态它的作品。 为什么?

再次,你不能让一个内部类静态,而是你可以声明一个静态类嵌套。 在这种情况下,这个嵌套类实际上是外部类的一部分,可以有静态成员没有任何问题。

内部类与静态嵌套类完全不同,尽pipe两者在语法上都是相似的。 静态嵌套类只是分组的一种手段,而内部类具有强关联 – 并且可以访问所有外部类的值。 你应该确定你为什么要使用一个内部类,然后它应该是非常自然的,你必须使用。 如果你需要声明一个静态方法,它可能是一个你想要的静态嵌套类。

这个话题引起了很多人的关注,我仍然会试着用最简单的话来解释。

首先,参照http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 ,在第一次发生/调用之前立即初始化类或接口任何以静态关键字开头的成员。

  1. 因此,如果我们在内部类中使用静态成员,将导致内部类的初始化,而不一定是外部/封闭类。 所以,我们阻碍了类的初始化顺序。

  2. 还要考虑一个事实,即非静态的内部类与一个封闭/外部类的实例相关联。 因此,与一个实例关联意味着,内部类将存在于一个Outer类实例中,并且在实例之间会有所不同。

简化这一点,为了访问静态成员,我们需要一个Outer类的实例,从这个实例我们将再次需要创build一个非静态内部类的实例。 静态成员不应该绑定到实例,因此您收到编译错误。

假设有两个外部类的实例,它们都实例化了内部类。现在,如果内部类有一个静态成员,那么它将只保留该成员在堆区的一个副本。在这种情况下,外部类的两个对象将引用此单一的副本和他们可以一起改变它。这可能会导致“脏读”的情况,因此,以防止这个Java已经应用这个限制。另一个支持这个论点的强项是,Java允许最终静态成员在这里,那些值不能从外部类对象中改变。 如果我错了,请让我。

首先为什么有人想在非静态内部类中定义静态成员? 答案是,所以外部类成员只能使用那些内部类名的静态成员,对吗?

但是对于这种情况,我们可以直接在外部类中定义成员。 这将与外部类实例中的内部类的所有对象相关联。

像下面的代码,

 public class Outer { class Inner { public static void method() { } } } 

可以这样写

 public class Outer { void method() { } class Inner { } } 

所以在我看来,不要让代码复杂化,Javadevise者不允许这个function,或者我们可能会在未来的版本中看到这个function。

试着把课堂当成一个普通的领域,然后你就会明白。

 //something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class Outer.something 

将内部类成员设置为静态是没有用的,因为您无法首先访问它们。

想想看,为了访问一个静态成员你使用className.memberName ,,在我们的例子中,它应该是像outerclassName.innerclassName.memberName ,,,现在你明白为什么innerclass必须是静态的….

您可以在静态嵌套类上使用静态方法。 例如

 public class Outer { public static class Inner { public static void method() { } } }