내부 클래스 객체에서 외부 클래스 객체 잡기


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에서 Qualifiedthis 로 설명됩니다 .

내부 클래스의 코드 외부에서 인스턴스를 가져 오는 방법 이 없다고 생각 합니다. 물론 언제든지 자신의 재산을 소개 할 수 있습니다.

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

편집 : 실험에 따르면 외부 클래스에 대한 참조를 보유하고있는 필드가 패키지 수준 액세스 권한을 가지고있는 것처럼 보입니다. 적어도 사용중인 JDK를 사용하십시오.

편집 : 사용 된 이름 ( this$0) Java에서 실제로 유효하지만 JLS 는 사용을 권장하지 않습니다.

$문자는 기계적으로 생성 된 소스 코드에서만 사용되거나 레거시 시스템의 기존 이름에 액세스하는 경우는 거의 없습니다.


고마워 존! 그러나 내부 클래스를 수정할 수있는 컨트롤이 없다면 어떻게해야합니까?
peakit

7
@peakit : 그렇다면 내가 아는 한, 반사를 사용하지 않으면 운이 없습니다. 실제로 캡슐화를 위반하는 것처럼 느껴집니다. 내부 클래스가 외부 인스턴스가 무엇인지 말하고 싶지 않다면 그것을 존중하고 필요하지 않도록 디자인해야합니다.
Jon Skeet

1
Java 8에서도 여전히 유효합니까?
안개 낀

@misty 예, 그렇습니다.
Hatefiend

36

OuterClass.this 외부 클래스를 참조합니다.


7
그러나 OuterClass의 소스 내외에서만. 그리고 나는 그것이 OP가 원하는 것이라고 생각하지 않습니다.
Stephen C

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의) 에서 main () 의 변수 xShadow.Test입니다 .

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

내부 클래스를 수정할 수있는 권한이없는 경우 리 펙션이 도움이 될 수 있습니다 (권장하지는 않음). this $ 0은 Inner 클래스의 참조로, Inner 클래스의 현재 인스턴스를 만드는 데 사용 된 Outer 클래스의 인스턴스를 알려줍니다.


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

물론 암시 적 참조의 이름은 신뢰할 수 없으므로 작업에 반영을 사용해서는 안됩니다.


'정적 내부'는 용어의 모순입니다.
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.