有多少外**这个**会有这个内心阶级?

时间:2012-04-16 19:52:42

标签: java inner-classes

我有以下内部类的情况。

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

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

然而,会发生什么是内部类派生自另一个内部类?

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

Inner1,Inner2会有自己的外部参考吗? 或者只是重用InnerBase中的一个,导致一个略微不同的行为?

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

4 个答案:

答案 0 :(得分:2)

在内部,层次结构中的每个非静态内部类都有自己对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);
}
BTW,如果你真的想要,你可以利用一些不起眼的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

答案 1 :(得分:1)

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

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());
    }
}

答案 2 :(得分:1)

让我们来看看该计划:

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,需要将一个封闭实例提供给构造函数。一般, 它是隐式提供的,但也可以使用超类显式提供 {@ 1}}

形式的构造函数调用

如果隐式提供封闭实例,编译器将生成表达式: 它使用this引用作为最里面的封闭类 超类是会员。诚然,这是相当满口的,但它是什么 编译器。在这种情况下,超类是expression.super(args)。因为目前的班级, Inner1,间接扩展Outer,它将Inner1作为继承成员。因此, 超类构造函数的限定表达式就是这个。该 编译器提供一个封闭的实例,重写this.super。

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

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

this

这是编译,但它很复杂。有一个更好的解决方案: 每当你写一个成员班时,问问自己,这个班级真的需要吗? 一个封闭的实例?如果答案是否定的,请将其设为静态。内部课程是 有时很有用,但它们很容易引入制作程序的复杂功能 很难理解。它们与泛型有很复杂的相互作用, 反思和继承。如果你声明public class Outer { class Inner1 extends Outer { } class Inner2 extends Inner1 { public Inner2() { Outer.this.super(); } } } 是静态的,问题就出现了 远。如果你还声明Inner1是静态的,你实际上可以理解是什么 该计划。

总之,一个类很少适合作为内部类和 另一个的子类。更一般地说,延伸内部很少是合适的 类;如果你必须,请仔细思考封闭的实例。大多数成员类可以而且应该被声明为静态。

答案 3 :(得分:0)

您的示例中只有一个级别的“内部”,因此每个内部类都可以访问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