【问题标题】:How many outer **this** will have this inner class?有多少个外部**this** 会有这个内部类?
【发布时间】:2012-04-16 19:52:41
【问题描述】:

我有以下内部类的情况。

class Outer {
   private class Inner1 extends InnerBase {
   }
   private class Inner2 extends InnerBase {
   }
   private class InnerBase {
   }
}

通常我认为内部类对外部类有一个额外的、隐藏的“this”。

但是,如果内部类是从另一个内部类派生的,会发生什么呢?

所有内部类(Inner1、Inner2、InnerBase)都应该有一个额外的 this。

Inner1、Inner2 是否有自己对外部 this 的引用? 或者只是重用 InnerBase 中的那个,导致行为略有不同?

(隐藏 this == 指向外部类实例的引用)

【问题讨论】:

  • 据我所知,this 总是指向你所在的类的实例。没有办法(除非你做一个)从内部类访问父类实例实例。我不太清楚你所说的“隐藏这个”是什么意思
  • @Lattyware 非静态嵌套类的每个实例都有对其创建它的封闭类实例的引用。这也是为什么您不能在静态方法中实例化非静态嵌套类型的原因。在示例中,您可以通过以下方式访问此封闭实例:Outer.this
  • @stefanbachert 你能解释一下行为差异在哪里吗?
  • 在第一种情况下,任何内部类都有一个隐藏的外部 this(浪费一些内存),在后面只有第一个内部类有一个隐藏的外部 this。
  • @MichaelSchmeißer 好吧,你可以在静态方法中实例化非静态嵌套类型,你只需要一个外部实例来使用奇怪的外部新语法:Outer o = new Outer(); Inner1 i1 = o.new Inner1();

标签: java inner-classes


【解决方案1】:

在内部,层次结构中的每个非静态内部类都有自己对this 的引用。您可以通过javap确认它

$ javap -private Outer.Inner1
Compiled from "Outer.java"
class Outer$Inner1 extends Outer$InnerBase{
    final Outer this$0;
    private Outer$Inner1(Outer);
}

$ javap -private Outer.InnerBase
Compiled from "Outer.java"
class Outer$InnerBase extends java.lang.Object{
    final Outer this$0;
    private Outer$InnerBase(Outer);
    Outer$InnerBase(Outer, Outer$1);
}

顺便说一句,如果你真的想要,你可以利用一些晦涩的 java 语法在父类和子类之间为 this$0 成员设置不同的值。方法如下:

public class Outer {
    class InnerBase {
        Outer innerBaseOuter() { return Outer.this; }
    }

    class Inner1 extends InnerBase {
        public Inner1() {}
        public Inner1(Outer parentOuter) {
            parentOuter.super(); // set a different super in InnerBase
        }
        Outer innerOuter() { return Outer.this; }
    }

    public static void main(String[] args) {
        Outer outer1 = new Outer();
        Outer outer2 = new Outer();

        Inner1 a = outer1.new Inner1();
        System.out.println("Checking (a.base == a.inner) => "+
            (a.innerBaseOuter() == a.innerOuter()));

        Inner1 b = outer1.new Inner1(outer2);
        System.out.println("Checking (b.base == b.inner) => "+
            (b.innerBaseOuter() == b.innerOuter()));
    }
}

运行你得到的程序:

Checking (a.base == a.inner) => true
Checking (b.base == b.inner) => false

【讨论】:

  • 看起来很有趣,但我有点困惑,你能解释一下吗? ^^
  • 我想知道为什么 InnerBase 有两个构造函数。然后我发现了:第二个是编译器插入的合成构造函数。私有类的默认构造函数是私有的,但任何 Inner1 构造函数仍必须调用 InnerBase 的非私有构造函数。因此,添加了具有默认可见性的合成构造函数。签名冲突(两个构造函数只需要 Outer 引用)通过添加另一个合成类型 Outer$1 的参数来解决。
  • javap -private 打印出已编译类文件的所有成员。当你编译一个内部类时,编译器会自动添加一个名为this$0 的内部字段来保存包含类的“this”引用。仔细想想,这并不神奇——运行时系统需要存储对包含类实例的引用,而自动生成的成员就是他们这样做的方式。
【解决方案2】:

每个都会引用同一个 Outer 实例。它们每个都有自己对 Outer 的引用,但该引用将指向 Outer 的同一个实例。由于无法更改引用,因此从其中任何一个引用外部 this 都是等效的。

class Outer {
    private class Inner1 extends InnerBase {
        Outer getOuter() {
            return Outer.this;
        }
    }

    private class Inner2 extends InnerBase {
        Outer getOuter() {
            return Outer.this;
        }
    }

    private class InnerBase {
        Outer getOuter() {
            return Outer.this;
        }
    }

    public static void main(String[] args) {
        new Outer().test();
    }
    public void test() {
        System.out.println((new Inner1()).getOuter());
        System.out.println((new Inner2()).getOuter());
        System.out.println((new InnerBase()).getOuter());
    }
}

【讨论】:

    【解决方案3】:

    让我们看一下程序:

    public class Outer 
    {
        public Outer() {}
        class Inner1 extends Outer 
        {
            public Inner1() 
            {
                super(); // invokes Object() constructor
            }
        }
    
        class Inner2 extends Inner1 
        {
            public Inner2() 
            {
                super(); // invokes Inner1() constructor
            }
        }
    }
    

    如果尝试编译它会发生错误。

    Outer.java:12: cannot reference this before
    supertype constructor has been called
    super(); // invokes Inner1() constructor
    

    因为Inner2 的超类本身就是一个内部类,一种晦涩难懂的语言 规则开始发挥作用。如您所知,内部类的实例化,例如 Inner1,需要为构造函数提供一个封闭实例。一般, 它是隐式提供的,但也可以通过超类显式提供 expression.super(args)形式的构造函数调用

    如果隐式提供封闭实例,编译器会生成表达式: 它将 this 引用用于最内层的封闭类,其中 超类是成员。诚然,这很拗口,但这就是 编译器会。在这种情况下,超类是Inner1。因为现在的班级, Inner2,间接扩展 Outer,它有 Inner1 作为继承成员。所以, 超类构造函数的限定表达式就是 this。这 编译器提供一个封闭实例,将 super 重写为 this.super。

    默认的Inner2构造函数尝试引用 this 在调用超类构造函数之前是非法的。

    解决这个问题的蛮力方法是提供合理的 显式封闭实例:

    public class Outer
    {
        class Inner1 extends Outer { }
        class Inner2 extends Inner1 
        {
            public Inner2() 
            {
                Outer.this.super();
            }
        }
    }
    

    这可以编译,但很复杂。有一个更好的解决方案: 每当你写一个成员类的时候,问问自己,这个类真的需要吗? 一个封闭的实例?如果答案是否定的,则将其设为静态。内部类是 有时很有用,但它们很容易引入复杂的程序,使程序 难以理解。它们与泛型有复杂的交互, 反射和继承。如果你声明Inner1 是静态的,问题就出在 离开。如果你还声明Inner2 是静态的,你实际上可以理解什么 程序会。

    总而言之,一个类既是内部类又是内部类很少适合 另一个的子类。更一般地说,很少适合扩展内部 班级;如果必须,请仔细考虑封闭的实例。大多数成员类可以并且应该声明为静态的。

    【讨论】:

      【解决方案4】:

      在您的示例中只有一层“内部”,因此每个内部类都可以访问您提到的 Outer 类。

      这是一个有趣的例子:

      public class Test {
      
          public static void main(String[] args) {
              Level1 l1 = new Level1();
              Level1.Level2 l2 = l1.new Level2();
              Level1.Level2.Level3 l3 = l2.new Level3();
          }
      
          public static class Level1 {
      
              private String s = "Level1";
      
              public Level1() {
                  System.out.println(this + ": " + s);
              }
      
              public class Level2 {
      
                  private String s = "Level2";
      
                  public Level2() {
                      System.out.println(this + ": " + s);
                      System.out.println("Level1: " + Level1.this);
                  }
      
                  public class Level3 extends OtherLevel {
      
                      private String s = "Level3";
      
                      public Level3() {
                          System.out.println(this + ": " + s);
                          System.out.println("Level1: " + Level1.this);
                          System.out.println("Level2: " + Level2.this);
      
                          System.out.println("super: " + super.toString());
                      }
                  }
              }
      
              public class OtherLevel {
      
                  private String s = "OtherLevel";
      
                  public OtherLevel() {
                      System.out.println(this + ": " + s);
                  }
              }
          }
      }
      

      输出:

      javaapplication4.Test$Level1@70284ac3: Level1
      javaapplication4.Test$Level1$Level2@74a14fed: Level2
      Level1: javaapplication4.Test$Level1@70284ac3
      javaapplication4.Test$Level1$Level2$Level3@88d00c6: OtherLevel
      javaapplication4.Test$Level1$Level2$Level3@88d00c6: Level3
      Level1: javaapplication4.Test$Level1@70284ac3
      Level2: javaapplication4.Test$Level1$Level2@74a14fed
      super: javaapplication4.Test$Level1$Level2$Level3@88d00c6
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-13
        • 1970-01-01
        • 1970-01-01
        • 2021-07-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多