【问题标题】:Invoking constructor of a Inner class from its subclass从子类调用内部类的构造函数
【发布时间】:2015-06-17 00:56:48
【问题描述】:

我正在学习内部课程。出于好奇,我扩展了具有参数化构造函数的内部类。但是当我写 super(int i) 来调用它时,代码不会编译。

class Outer{
  class Inner{
    private int a;
    Inner(int i){
     this.a=i;
    }

    public void inheritFromInner(){
      System.out.println("Inherited from inner");
    }
  }       
}

class Test extends Outer.Inner{
  Test(int r){
   super(r);// This line does not compile.
  }
}

由于内部类是外部类的一部分(成员),它们必须通过外部类访问。如何调用Test类的超级构造函数。

编译错误是:由于一些中间构造函数调用,没有可用的外部类型的封闭实例

【问题讨论】:

  • 编译器给你的错误信息是什么?这是您要编译的确切代码吗?
  • @matt : 由于一些中间构造函数调用,没有可用的 Outer 类型的封闭实例
  • 你的内部类要求它有一个外部类的实例不会因为你继承它而消失。
  • 我试过这个..... new Outer().super(r); ..它起作用了,调用了内部类的构造函数... :-)

标签: java inner-classes


【解决方案1】:

现有的答案没有说明成员类之间的差异。

成员类是在另一个类中声明的任何类。它们有时被称为嵌套类。在成员类的种类中,匿名内部类和本地类在此不再赘述。其余的成员类有内部类和静态成员类。

  • 内部类。内部类实例必须使用现有外部类实例的上下文进行实例化。内部类包含对外部类实例的隐式引用(这一事实可能导致内部类实例导致内存泄漏)。虽然不常见,但您可以按如下方式实例化内部类实例:

    Outer out = new Outer();
    Outer.Inner inner = out.new Inner();
    

    因为super() 关键字必须是在子类构造函数中执行的第一行,所以如果它确实需要在内部类中,则必须像这样声明构造函数:

    class Test extends Outer.Inner{
        Test(Outer out, int r) {
            out.super(r);
        }
    }
    
  • 静态成员类。静态成员类在类声明中使用修饰符 static 声明。在几乎所有重要的方面,静态成员类都是顶级类。像顶级类(与内部类不同)一样,它们不需要任何其他对象实例来实例化。静态成员类对象通常被实例化为:

    Outer.Inner inner = new Outer.Inner();
    

只要有可能,您应该强烈支持使用静态成员类而不是内部类。仅当您的成员类对象确实必须具有超类实例(这应该不常见)时才使用内部类。

【讨论】:

    【解决方案2】:

    你需要将你的 Inner 类声明为public static

    class Outer{
      public static class Inner{
        private int a;
        Inner(int i){
         this.a=i;
        }
    
        public void inheritFromInner(){
          System.out.println("Inherited from inner");
        }
      }       
    }
    
    class Test extends Outer.Inner{
      Test(int r){
       super(r);// This line does not compile.
      }
    }
    

    【讨论】:

      【解决方案3】:

      该错误实际上与该特定代码行无关,也与超类构造函数接受参数无关。你可以用一个更简单的例子重现错误:

      class Outer {
          class Inner {
          }       
      }
      
      class Test extends Outer.Inner {
      }
      

      这会给出一个错误,例如:

      Outer.java:6: error: an enclosing instance that contains Outer.Inner is required
      class Test extends Outer.Inner {
      ^
      1 error
      

      有两种类型的内部类,非静态(默认)和静态(需要关键字static)。静态内部类就像顶级类(始终是静态的),但非静态内部类需要其封闭类的实例才能被实例化。由于Outer.Inner 是一个非静态内部类,它只能在Outer 实例的“上下文”中实例化,这意味着要么在Outer 的实例方法内部,要么通过以下代码:

      Outer outer = new Outer();
      outer.new Inner();
      

      (这种语法并不常见。)

      由于Test是顶级类,它是静态的,所以不能子类化非静态的内部类,因为通过new Test()创建实例时,不会有@的实例987654332@提供。

      但请注意,此代码编译时不会出错:

      class Outer {
          class Inner {
          }
      
          class Test extends Outer.Inner {
          }
      }
      

      此时Test也是Outer的非静态内部类,所以Test的实例必须在Outer的实例的上下文中创建,满足其超类的要求.

      当然,修改原代码使Inner静态也可以编译。在实际代码中,您创建一个非静态内部类,因为您需要该类的一个实例才能访问封闭类的关联实例。在这种情况下,您无法选择将其设为静态。但是如果它不需要这样的访问,那么你应该让你的内部类静态(或者,让它成为一个顶级类)。

      编辑: 显然,可以通过将必要的封闭类的实例作为构造函数参数显式传递给子类并调用 @987654339 来创建非静态内部类的静态子类@ 在上面。见scottb的回答here

      【讨论】:

      • “由于 Test 是一个顶级类,它是静态的,因此它不能子类化非静态内部类” - 这不是真的。请参阅@scottb 的答案。尤其是关于out.super(r);的部分
      • @ErwinBolwidt 有趣,我不知道。我已对我的答案进行了更正。
      猜你喜欢
      • 1970-01-01
      • 2016-05-08
      • 1970-01-01
      • 1970-01-01
      • 2014-02-19
      • 2021-09-19
      • 2018-05-25
      • 2018-02-28
      • 1970-01-01
      相关资源
      最近更新 更多