3

我有以下内部类的情况。

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 == 指向外部类实例的引用)

4

4 回答 4

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

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

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
于 2012-04-16T19:57:11.133 回答
1

每个都会引用相同的 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());
    }
}
于 2012-04-16T20:05:22.317 回答
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,需要将封闭实例提供给构造函数。通常,它是隐式提供的,但也可以通过超类构造函数调用形式显式提供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();
        }
    }
}

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

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

于 2012-04-16T20:25:56.267 回答
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
于 2012-04-16T19:58:39.577 回答