การได้รับของวัตถุชั้นนอกจากวัตถุชั้นใน


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 ว่าผ่านการรับรองthisแล้ว

ฉันไม่คิดว่าจะมีวิธีรับอินสแตนซ์จากนอกโค้ดของคลาสภายใน แน่นอนคุณสามารถแนะนำทรัพย์สินของคุณเอง:

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

แก้ไข: โดยการทดลองดูเหมือนว่าเขตข้อมูลที่อ้างอิงถึงคลาสภายนอกมีการเข้าถึงระดับแพ็กเกจอย่างน้อยกับ JDK ที่ฉันใช้อยู่

แก้ไข: ชื่อที่ใช้ ( this$0) ใช้งานได้จริงใน Java แม้ว่าJLS จะไม่สนับสนุนการใช้งาน:

$ตัวอักษรควรใช้เฉพาะในรหัสที่มาสร้างกลไกหรือไม่ค่อยเข้าถึงชื่อที่มีอยู่ก่อนในระบบเดิม


ขอบคุณจอน! แต่ถ้าฉันไม่สามารถควบคุมการแก้ไขชั้นใน (ตรวจสอบการแก้ไขของฉัน)
จุดสูงสุด

7
@peakit: เท่าที่ฉันรู้คุณไม่มีโชคเว้นแต่คุณใช้การสะท้อน รู้สึกเหมือนเป็นการละเมิด encapsulation แต่จริง ๆ - ถ้าชั้นในไม่ต้องการบอกคุณว่าอินสแตนซ์ด้านนอกของมันคืออะไรคุณควรเคารพสิ่งนั้นและพยายามออกแบบในแบบที่คุณไม่ต้องการ
Jon Skeet

1
ยังคงใช้ได้ใน Java 8 หรือไม่
หมอก

@misty ใช่มันเป็น
เกลียด

36

OuterClass.this อ้างอิงคลาสภายนอก


7
แต่เฉพาะใน / ภายในแหล่งที่มาของ OuterClass และฉันไม่คิดว่าเป็นสิ่งที่ OP ต้องการ
สตีเฟ่นซี

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) ตัวแปรxในmain ()คือshadowing 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();
    }
}

การรันโปรแกรมนี้จะพิมพ์:

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.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

หากคุณไม่มีอำนาจในการปรับเปลี่ยนคลาสภายในการแก้ไขอาจช่วยคุณได้ (แต่ไม่แนะนำ) $ 0 นี้อ้างอิงในชั้นในซึ่งบอกว่าอินสแตนซ์ของชั้นนอกถูกใช้เพื่อสร้างอินสแตนซ์ปัจจุบันของชั้นใน


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

แน่นอนชื่อของการอ้างอิงโดยนัยนั้นไม่น่าเชื่อถือดังนั้นคุณไม่ควรใช้การสะท้อนกลับสำหรับงาน


'Static Inner' เป็นความขัดแย้งในแง่
มาร์ควิสแห่ง Lorne

-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.