Получение внешнего объекта класса от внутреннего объекта класса

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

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

peakit
источник

Ответы:

329

Внутри самого внутреннего класса вы можете использовать OuterClass.this. Это выражение, которое позволяет ссылаться на любой лексически включающий экземпляр, описано в JLS как квалифицированноеthis .

Я не думаю, что есть способ получить экземпляр извне кода внутреннего класса. Конечно, вы всегда можете ввести свою собственность:

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

РЕДАКТИРОВАТЬ: экспериментально, похоже, что поле, содержащее ссылку на внешний класс имеет доступ на уровне пакета - по крайней мере с JDK, который я использую.

EDIT: Имя , используемое ( this$0) является на самом деле действует в Java, хотя JLS рекомендует его использование:

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

Джон Скит
источник
Спасибо Джон! Но что, если у меня нет контроля над изменением внутреннего класса (проверьте мои правки).
пик
7
@peakit: Тогда, насколько я знаю, вам не повезло, если вы не используете отражение. Такое ощущение, что это нарушение инкапсуляции, хотя на самом деле - если внутренний класс не хочет сообщать вам, каков его внешний экземпляр, вы должны уважать это и пытаться спроектировать так, чтобы он вам не нужен.
Джон Скит
1
Это все еще действует в Java 8?
Мисти
@misty Да, это так.
Hatefiend
36

OuterClass.this ссылается на внешний класс.

bmargulies
источник
7
Но только в / внутри источника OuterClass. И я не думаю, что этого хочет ОП.
Стивен С
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 () является теневым для 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

Gladclef
источник
Не уверен, что этот пример лучше всего подтверждает эту точку, потому что «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 является ссылкой во внутреннем классе, который сообщает, какой экземпляр класса 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 :-( ");
        }
    }
}
kyakya
источник