Bắt giữ đối tượng lớp bên ngoài từ đối tượng lớp bên trong


245

Tôi có mã sau đây. Tôi muốn có được đối tượng lớp bên ngoài bằng cách sử dụng mà tôi đã tạo đối tượng lớp bên trong inner. Tôi làm nó như thế nào?

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: Vâng, một số bạn đề nghị sửa đổi lớp bên trong bằng cách thêm một phương thức:

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

Nhưng nếu tôi không có quyền kiểm soát để sửa đổi lớp bên trong, thì (chỉ để xác nhận) liệu chúng ta có cách nào khác để lấy đối tượng lớp bên ngoài tương ứng từ đối tượng lớp bên trong không?

Câu trả lời:


329

Trong chính lớp bên trong, bạn có thể sử dụng OuterClass.this. Biểu thức này, cho phép tham chiếu đến bất kỳ trường hợp bao quanh từ vựng nào, được mô tả trong JLS là Đủ điều kiệnthis .

Tôi không nghĩ rằng có một cách để lấy ví dụ từ bên ngoài mã của lớp bên trong. Tất nhiên, bạn luôn có thể giới thiệu tài sản của riêng bạn:

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

EDIT: Bằng thử nghiệm, có vẻ như trường giữ tham chiếu đến lớp bên ngoài có quyền truy cập cấp gói - ít nhất là với JDK tôi đang sử dụng.

EDIT: Tên sử dụng ( this$0) thực sự có giá trị trong Java, mặc dù JLS không khuyến khích việc sử dụng nó:

$tự chỉ nên được sử dụng trong mã nguồn được tạo cơ học hoặc, hiếm khi, để truy cập các tên có sẵn trên các hệ thống cũ.


Cảm ơn Jon! Nhưng nếu tôi không có quyền kiểm soát để sửa đổi lớp bên trong (hãy kiểm tra chỉnh sửa của tôi).
đỉnh

7
@peakit: Theo như tôi biết, bạn không gặp may trừ khi bạn sử dụng phản xạ. Có vẻ như đó là vi phạm đóng gói mặc dù thực sự - nếu lớp bên trong không muốn cho bạn biết ví dụ bên ngoài của nó là gì, bạn nên tôn trọng điều đó và cố gắng thiết kế sao cho bạn không cần nó.
Jon Skeet

1
Điều này có còn hợp lệ trong Java 8 không?
sương mù

@misty Vâng, đúng vậy.
Ghét bạn bè

36

OuterClass.this tham khảo các lớp bên ngoài.


7
Nhưng chỉ trong / trong nguồn của OuterClass. Và tôi không nghĩ đó là những gì OP muốn.
Stephen C

23

Bạn có thể (nhưng bạn không nên) sử dụng sự phản chiếu cho công việc:

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

Tất nhiên, tên của tài liệu tham khảo ngầm hoàn toàn không đáng tin cậy, vì vậy như tôi đã nói, bạn không nên :-)


2

Câu trả lời chung hơn cho câu hỏi này liên quan đến các biến bị che khuất và cách chúng được truy cập.

Trong ví dụ sau (từ Oracle), biến x trong hàm main () đang tạo bóng 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();
    }
}

Chạy chương trình này sẽ in:

x=0, Test.this.x=1

Xem thêm tại: http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6


Không chắc chắn rằng ví dụ đó chứng minh điểm tốt nhất vì "Test.this.x" giống như "Test.x" vì nó là tĩnh, không thực sự thuộc về đối tượng lớp kèm theo. Tôi nghĩ sẽ là ví dụ tốt hơn nếu mã nằm trong hàm tạo của lớp Test và Test.x không tĩnh.
sb4

0

Đây là ví dụ:

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

nếu bạn không có quyền kiểm soát để sửa đổi lớp bên trong, việc chỉnh sửa có thể giúp bạn (nhưng không khuyến nghị). $ 0 này là tham chiếu trong lớp bên trong cho biết trường hợp nào của lớp ngoài được sử dụng để tạo phiên bản hiện tại của lớp bên trong.


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

Tất nhiên, tên của tài liệu tham khảo ngầm là không đáng tin cậy, vì vậy bạn không nên sử dụng sự phản chiếu cho công việc.


"Nội tĩnh" là một mâu thuẫn trong các điều khoản.
Hầu tước Lorne

-2

Đã được chỉnh sửa vào năm 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 :-( ");
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.