内部クラスオブジェクトから外部クラスオブジェクトを取得する


245

次のコードがあります。内部クラスオブジェクトの作成に使用した外部クラスオブジェクトを取得しますinner。どうすればできますか?

public class OuterClass {

    public class InnerClass {
        private String name = "Peakit";
    }

    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        InnerClass inner = outer.new InnerClass();
       // How to get the same outer object which created the inner object back?
        OuterClass anotherOuter = ?? ;

        if(anotherOuter == outer) {
             System.out.println("Was able to reach out to the outer object via inner !!");
        } else {
             System.out.println("No luck :-( ");
        }
    }
}

編集:まあ、あなたたちの何人かはメソッドを追加して内部クラスを変更することを提案しました:

public OuterClass outer() {
   return OuterClass.this;
}

しかし、内部クラスを変更するコントロールがない場合、(確認のために)内部クラスオブジェクトから対応する外部クラスオブジェクトを取得する別の方法がありますか?

回答:


329

内部クラス自体では、を使用できますOuterClass.this。字句的に囲まれたインスタンスを参照できるこの式は、JLSでQualifiedthisとして記述されています。

ただし、内部クラスのコードの外部からインスタンスを取得する方法はないと思います。もちろん、いつでも独自のプロパティを導入できます。

public OuterClass getOuter() {
    return OuterClass.this;
}

編集:実験により、外部クラスへの参照を保持するフィールドにはパッケージレベルのアクセス権があるようです-少なくとも私が使用しているJDKでは。

編集:使用される名前(this$0実際にはJavaで有効ですが、JLSはその使用を推奨していません。

この$文字は、機械的に生成されたソースコードでのみ使用するか、レガシーシステムで既存の名前にアクセスすることはほとんどありません。


ジョンありがとう!しかし、内部クラスを変更するコントロールがない場合はどうなりますか(編集内容を確認してください)。
ピーク

7
@peakit:それから、私が知る限り、リフレクションを使用しない限り、あなたは運が悪いです。カプセル化の違反のように思えますが、内部クラスが外部インスタンスを教えたくない場合は、それを尊重し、必要のないように設計する必要があります。
Jon Skeet、

1
これはJava 8でも有効ですか?
霧の深い2016

@mistyはい、そうです。
Hatefiend

36

OuterClass.this 外部クラスを参照します。


7
ただし、OuterClassのソース内/内のみ。そして、それはOPが望んでいることではないと思います。
スティーブンC

23

あなたは仕事にリフレクションを使うことができます(しかしあなたはすべきではありません):

import java.lang.reflect.Field;

public class Outer {
    public class Inner {
    }

    public static void main(String[] args) throws Exception {

        // Create the inner instance
        Inner inner = new Outer().new Inner();

        // Get the implicit reference from the inner to the outer instance
        // ... make it accessible, as it has default visibility
        Field field = Inner.class.getDeclaredField("this$0");
        field.setAccessible(true);

        // Dereference and cast it
        Outer outer = (Outer) field.get(inner);
        System.out.println(outer);
    }
}

もちろん、暗黙の参照の名前は完全に信頼できないので、私が言ったように、あなたはすべきではありません:-)


2

この質問に対するより一般的な答えは、シャドウされた変数とそれらへのアクセス方法を含みます。

次の例(Oracleの場合)では、main()の変数xTest.xをシャドウしています

class Test {
    static int x = 1;
    public static void main(String[] args) {
        InnerClass innerClassInstance = new InnerClass()
        {
            public void printX()
            {
                System.out.print("x=" + x);
                System.out.println(", Test.this.x=" + Test.this.x);
            }
        }
        innerClassInstance.printX();
    }

    public abstract static class InnerClass
    {
        int x = 0;

        public InnerClass() { }

        public abstract void printX();
    }
}

このプログラムを実行すると、次の内容が出力されます。

x=0, Test.this.x=1

詳細:http : //docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6


「Test.this.x」は静的であるため「Test.x」と同じであり、実際には囲んでいるクラスオブジェクトに属していないため、この例が最も適切な点を証明しているとは限りません。コードが静的ではなくクラスTestおよびTest.xのコンストラクターにあった場合、より良い例になると思います。
sb4

0

次に例を示します。

// Test
public void foo() {
    C c = new C();
    A s;
    s = ((A.B)c).get();
    System.out.println(s.getR());
}

// classes
class C {}

class A {
   public class B extends C{
     A get() {return A.this;}
   }
   public String getR() {
     return "This is string";
   }
}

0

内部クラスを変更するコントロールがない場合は、再帰が役立つことがあります(ただし、お勧めしません)。this $ 0は、Innerクラスの現在のインスタンスを作成するために使用されたOuterクラスのインスタンスを通知するInnerクラスの参照です。


-1
/**
 * Not applicable to Static Inner Class (nested class)
 */
public static Object getDeclaringTopLevelClassObject(Object object) {
    if (object == null) {
        return null;
    }
    Class cls = object.getClass();
    if (cls == null) {
        return object;
    }
    Class outerCls = cls.getEnclosingClass();
    if (outerCls == null) {
        // this is top-level class
        return object;
    }
    // get outer class object
    Object outerObj = null;
    try {
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            if (field != null && field.getType() == outerCls
                    && field.getName() != null && field.getName().startsWith("this$")) {
                field.setAccessible(true);
                outerObj = field.get(object);
                break;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return getDeclaringTopLevelClassObject(outerObj);
}

もちろん、暗黙的な参照の名前は信頼できないため、ジョブにリフレクションを使用しないでください。


「静的インナー」は、言葉の矛盾です。
ローン侯爵

-2

2020-06-15に編集されました

public class Outer {

    public Inner getInner(){
        return new Inner(this);
    }

    static class Inner {

        public final Outer Outer;

        public Inner(Outer outer) {
            this.Outer=outer;
        }
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        Inner inner = outer.getInner();
        Outer anotherOuter=inner.Outer;

        if(anotherOuter == outer) {
            System.out.println("Was able to reach out to the outer object via inner !!");
        } else {
            System.out.println("No luck :-( ");
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.