代码之家  ›  专栏  ›  技术社区  ›  Rahul Garg

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

  •  137
  • Rahul Garg  · 技术社区  · 17 年前

    如果我将内部类设为静态,它就会工作。为什么?

    14 回复  |  直到 6 年前
        1
  •  112
  •   Bill the Lizard    10 年前

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

        2
  •  48
  •   Eddie    13 年前

    在非静态内部类中允许静态方法没有多大意义;您将如何访问它?如果不经过外部类实例,则无法访问(至少最初)非静态内部类实例。没有纯粹静态的方法来创建非静态的内部类。

    外流阶级 Outer ,您可以访问静态方法 test()

    Outer.test();
    

    对于静态内部类 Inner innerTest()

    Outer.Inner.innerTest();
    

    然而,如果 内部的 innertest . 非静态内部类绑定到其外部类的特定实例。函数不同于常数,因为它是对 Outer.Inner.CONSTANT Outer.Inner.staticFunction(); 事实并非如此。假设你有 Inner.staticFunction() 那叫 getState() ,在中定义 外面的

    Paul Bellora是正确的,语言设计者本可以允许这样做。然后,他们必须小心地禁止在非静态内部类的静态方法中访问对外部类的隐式引用。在这一点上,如果不能引用外部类(静态引用除外),那么作为一个内部类,它的值是什么?如果静态访问很好,那么为什么不将整个内部类声明为静态呢?如果您只是将内部类本身设置为静态,那么您就没有对外部类的隐式引用,也不再有这种模糊性。

    如果你真的 需要 非静态内部类上的静态方法,那么您可能需要重新考虑您的设计。

        3
  •  21
  •   gustafc    17 年前

    首先,您应该了解一些关于如何在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

        4
  •  7
  •   Don Li    14 年前

    唯一的原因是“不是必须的”,那么为什么还要费心支持它呢?

    Inner 与的实例关联 Outer ,仍然可以使用 Outer.Inner.myStatic 如果java决定这样做。

    如果您需要在的所有实例中共享某些内容 内部的 ,你可以把它们放进 外面的 作为静态成员。这并不比在中使用静态成员更糟糕 哪里 内部的 无论如何(不改进封装)。

    如果您需要在的所有实例中共享某些内容 内部的 由某人创造 outer 外面的

    我不同意“静态嵌套类实际上只是一个顶级类”的观点。我认为最好将静态嵌套类/内部类视为外部类的一部分,因为它们可以访问外部类的私有成员。外部阶级的成员也是“内部阶级的成员”。所以不需要在内部类中支持静态成员。外部类中的普通/静态成员就足够了。

        5
  •  4
  •   Benjamin Curtis Drake    7 年前

    发件人: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

    与实例方法和变量一样,内部类与其封闭类的实例相关联,并且可以直接访问该对象的方法和字段。此外,由于内部类与实例相关联,因此它本身无法定义任何静态成员。

    甲骨文的解释是肤浅和徒劳的。由于没有技术或语法上的理由来抢占内部类中的静态成员(其他语言如C#也允许这样做),Java设计者的动机可能是概念品味和/或技术便利。

    以下是我的猜测:

    与顶级类不同,内部类依赖于实例:内部类实例与其每个外部类的实例相关联,并且可以直接访问其成员。这是在Java中使用它们的主要动机。用另一种方式表示:内部类是 在外部类实例的上下文中实例化。没有外部类实例,内部类就不应该再存在了 而不是外部类的其他实例成员。让我们将其称为 属于内部阶级。

    静态成员(不是面向对象的)的本质与 依赖实例

    实例依赖精神 抢占这一更深层次的OOP攻击的额外好处。

    实例依赖精神 ? 也许是因为 常数 不需要占用超过必要的内存(如果它们被强制为非静态的,那么它们会被复制到每个内部类实例中,这可能会造成浪费)。否则我无法想象例外的原因。

    这可能不是坚如磐石的推理,但在我看来,它最能理解甲骨文对此事的草率评论。

        6
  •  3
  •   Russell Zahniser    14 年前

    需要内部类中的静态成员的主要原因是为了代码的干净性——仅由内部类使用的静态成员应该位于内部,而不必放置在外部类中。考虑:

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

    当我使用不合格的标识符“OUTID”时,考虑GETIE()中的内容。此标识符出现的范围类似于:

    Outer -> Inner -> getID()
    

    在这里,同样因为javac就是这样工作的,所以作用域的“外部”级别包括外部的静态和实例成员。这是令人困惑的,因为我们通常被告知将类的静态部分视为作用域的另一个级别:

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

    好的,这与内部类有什么关系?天真地说,我们认为内部类没有理由不能有一个静态作用域,因为我们将作用域描绘成这样:

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

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

    javac不是这样工作的——静态和实例成员都有一个单一级别的作用域,并且作用域总是严格嵌套的。甚至继承也是通过将声明复制到子类而不是分支和搜索超类范围来实现的。

    为了支持内部类的静态成员,javac必须拆分静态范围和实例范围 支持分支和重新连接作用域层次结构,否则它必须扩展其简单的布尔“静态上下文”思想,以更改跟踪当前作用域中所有嵌套类级别的上下文类型。

        7
  •  3
  •   akhil_mittal    10 年前

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

    非静态嵌套类称为内部类,因此您没有 non-static inner class

    如果没有外部类的对应实例,内部类实例就不存在。内部类不能声明编译时常量以外的静态成员。如果允许的话,这个词的意思就会模棱两可 static . 在这种情况下,可能会出现某些混乱:

    1. 或者每个外部对象只有一个实例?

    这就是为什么设计师可能决定根本不处理这个问题。

    同样,不能使内部类成为静态的,而可以将静态类声明为嵌套的。在这种情况下,这个嵌套类实际上是外部类的一部分,可以有静态成员,而不会出现任何问题。

        8
  •  3
  •   Doomed93    9 年前

    这个话题已经引起了很多人的注意,但我还是会尝试用最简单的术语来解释。

    首先,关于 http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1

    1. 因此,如果我们在内部类中放置一个静态成员,它将导致内部类的初始化,而不一定是外部/封闭类。因此,我们妨碍了类的初始化顺序。

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

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

        9
  •  2
  •   B.E.    17 年前

        10
  •  2
  •   DuncG    5 年前

    要向其中添加记录的工作 JDK16 还提到静态方法和字段现在可以与内部类一起使用,即使允许 main() 开始上课。

    java Outer java Outer$Inner

    public class Outer {
        public static void main(String[] args) {
            System.out.println("Outer class main xxx="+Inner.xxx+" nnn="+(++Inner.nnn)+" iii="+(--iii));
            aaa();
            Inner.zzz();
        }
        public static void aaa() {
            System.out.println("aaa() nnn="+(++Inner.nnn)+" iii="+(--iii));
        }
        public static int iii = 100;
    
        class Inner {
            public static final String xxx= "yyy";
            public static int nnn = 0;
    
            public static void zzz() {
                System.out.println("zzz() "+" nnn="+(++nnn)+" iii="+(--iii));
            }
            public static void main(String[] args) {
                System.out.println("Inner class main xxx="+xxx+" nnn="+(++nnn)+" iii="+(--iii));
                zzz();
                aaa();
            }
        }
    }
    
        11
  •  0
  •   Malay    16 年前

    假设有两个外部类的实例&它们都实例化了内部类。现在,如果内部类有一个静态成员,那么它将在堆区域中只保留该成员的一个副本。在这种情况下,外部类的两个对象都将引用这个单一副本&它们可以一起修改。这可能会导致“脏读”情况,从而阻止此Java应用此限制。支持此论点的另一个优点是Java允许此处的最终静态成员,即其值不能从任何外部类对象更改的成员。 如果我错了,请一定要告诉我。

        12
  •  0
  •   Vishnu Saini    10 年前

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

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

    可以这样写吗

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

        13
  •  -1
  •   user1888955    9 年前

    试着把这个类当作一个普通的字段,然后你就会明白了。

    //something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
    Outer.something 
    
        14
  •  -2
  •   mR_fr0g    17 年前

    允许在静态嵌套类上使用静态方法。例如

    public class Outer {
    
      public static class Inner {
    
        public static void method() {
    
        }
      }
    }
    
        15
  •  -2
  •   Creative_Cimmons    11 年前

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

    推荐文章