Mendapatkan objek kelas luar dari objek kelas dalam

245

Saya memiliki kode berikut. Saya ingin mendapatkan objek kelas luar menggunakan yang saya buat objek kelas dalam inner. Bagaimana saya bisa melakukannya?

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

EDIT: Ya, beberapa dari kalian menyarankan untuk memodifikasi kelas dalam dengan menambahkan metode:

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

Tetapi bagaimana jika saya tidak memiliki kontrol untuk memodifikasi kelas dalam, maka (hanya untuk mengkonfirmasi) apakah kita memiliki cara lain untuk mendapatkan objek kelas luar yang sesuai dari objek kelas dalam?

peakit
sumber

Jawaban:

329

Di dalam kelas batin itu sendiri, Anda bisa menggunakannya OuterClass.this. Ungkapan ini, yang memungkinkan untuk merujuk pada setiap instance yang tertutup secara leksikal, dijelaskan dalam JLS sebagai Berkualitasthis .

Saya tidak berpikir ada cara untuk mendapatkan contoh dari luar kode kelas dalam. Tentu saja, Anda selalu dapat memperkenalkan properti Anda sendiri:

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

EDIT: Dengan eksperimen, sepertinya bidang memegang referensi ke kelas luar memiliki akses tingkat paket - setidaknya dengan JDK yang saya gunakan.

EDIT: Nama yang digunakan ( this$0) adalah benar-benar berlaku di Jawa, meskipun JLS enggan penggunaannya:

The $karakter harus digunakan hanya dalam kode sumber mekanis yang dihasilkan atau, jarang, untuk mengakses nama yang sudah ada pada sistem warisan.

Jon Skeet
sumber
Terima kasih jon! Tetapi bagaimana jika saya tidak memiliki kontrol untuk memodifikasi kelas dalam (periksa edit saya).
peakit
7
@peakit: Lalu sejauh yang saya tahu, Anda kurang beruntung kecuali Anda menggunakan refleksi. Rasanya itu adalah pelanggaran enkapsulasi meskipun benar - jika kelas dalam tidak ingin memberi tahu Anda apa contoh luarnya, Anda harus menghargai itu dan mencoba merancang sedemikian rupa sehingga Anda tidak membutuhkannya.
Jon Skeet
1
Apakah ini masih berlaku di Java 8?
berkabut
@misty Ya, benar.
Hatefiend
36

OuterClass.this referensi kelas luar.

bmargulies
sumber
7
Tetapi hanya di / dalam sumber OuterClass. Dan saya tidak berpikir itu yang diinginkan OP.
Stephen C
23

Anda bisa (tetapi tidak seharusnya) menggunakan refleksi untuk pekerjaan itu:

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

Tentu saja, nama referensi implisit sama sekali tidak dapat diandalkan, jadi seperti yang saya katakan, Anda tidak boleh :-)

Lukas Eder
sumber
2

Jawaban yang lebih umum untuk pertanyaan ini melibatkan variabel gelap dan bagaimana mereka diakses.

Dalam contoh berikut (dari Oracle), variabel x di main () membayangi Test.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();
    }
}

Menjalankan program ini akan mencetak:

x=0, Test.this.x=1

Lebih lanjut di: http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6

Gladclef
sumber
Tidak yakin bahwa contoh membuktikan titik terbaik karena "Test.this.x" sama dengan "Test.x" karena itu statis, tidak benar-benar milik objek kelas terlampir. Saya pikir akan menjadi contoh yang lebih baik jika kode itu dalam konstruktor kelas Test dan Test.x tidak statis.
sb4
0

Inilah contohnya:

// 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";
   }
}
Ashish Rawat
sumber
0

jika Anda tidak memiliki kontrol untuk memodifikasi kelas dalam, refection dapat membantu Anda (tetapi tidak merekomendasikan). $ 0 ini adalah referensi dalam kelas dalam yang memberitahu instance kelas luar mana yang digunakan untuk membuat instance kelas batin saat ini.

曹建 发
sumber
-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);
}

Tentu saja, nama referensi implisit tidak dapat diandalkan, jadi Anda tidak harus menggunakan refleksi untuk pekerjaan itu.

Vali Zhao
sumber
'Static inner' adalah kontradiksi dalam istilah.
Marquis of Lorne
-2

Telah diedit pada 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 :-( ");
        }
    }
}
kyakya
sumber