Отримання об'єкта зовнішнього класу від об'єкта внутрішнього класу


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

EDIT: Ну, деякі з вас, хлопці, запропонували змінити внутрішній клас, додавши метод:

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

Але що робити, якщо я не маю можливості змінювати внутрішній клас, то (лише для підтвердження) чи є у нас ще якийсь спосіб отримання відповідного об'єкта зовнішнього класу від об'єкта внутрішнього класу?

Відповіді:


329

У самому внутрішньому класі ви можете використовувати OuterClass.this. Цей вираз, який дозволяє посилатися на будь-який екземпляр, що охоплює лексику, описується в JLS як кваліфікованийthis .

Я не думаю, що існує спосіб отримати екземпляр поза кодом внутрішнього класу. Звичайно, ви завжди можете представити власну власність:

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

EDIT: Експериментуючи, схоже, що поле, яке посилається на зовнішній клас, має доступ до рівня пакетів - принаймні з JDK, який я використовую.

EDIT: Ім'я, що використовується ( this$0) , дійсно дійсне в Java, хоча JLS перешкоджає його використанню:

$Символ повинен використовуватися тільки в механічно створеному вихідному коді або, рідше, до під'їзних вже існуючим іменах на застарілих системах.


Дякую, Джон! Але що робити, якщо я не маю можливості змінювати внутрішній клас (перевірити мою редагування).
пік

7
@peakit: Тоді, наскільки я знаю, вам не пощастило, якщо ви не використовуєте роздуми. Схоже, це порушення інкапсуляції, хоча насправді - якщо внутрішній клас не хоче сказати вам, що таке його зовнішній екземпляр, ви повинні це поважати і намагатися спроектувати таке, що вам не потрібно.
Джон Скіт

1
Чи все-таки це дійсно в Java 8?
Туманний

@misty Так, так.
Hatefiend


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 in main () є відтінком 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 і 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);
}

Звичайно, ім’я неявної посилання є недостовірним, тому для роботи не слід використовувати роздуми.


"Статичний внутрішній" - це протиріччя в термінах.
Маркіз Лорн

-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 :-( ");
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.