В Java, как я могу получить доступ к внешнему классу, когда я не во внутреннем классе? - PullRequest
32 голосов
/ 18 апреля 2009

Если у меня есть экземпляр внутреннего класса, как я могу получить доступ к внешнему классу из кода, которого нет во внутреннем классе ? Я знаю, что во внутреннем классе я могу использовать Outer.this, чтобы получить внешний класс, но я не могу найти какой-либо внешний способ получить это.

Например:

public class Outer {
  public static void foo(Inner inner) {
    //Question: How could I write the following line without
    //  having to create the getOuter() method?
    System.out.println("The outer class is: " + inner.getOuter());
  }
  public class Inner {
    public Outer getOuter() { return Outer.this; }
  }
}

Ответы [ 8 ]

32 голосов
/ 18 апреля 2009

Байт-код класса Outer$Inner будет содержать поле области пакета с именем this$0 типа Outer. Вот как в Java реализованы нестатические внутренние классы, потому что на уровне байт-кода отсутствует понятие внутреннего класса.

Вы должны быть в состоянии прочитать это поле, используя отражение, если вы действительно хотите. У меня никогда не было необходимости делать это, поэтому было бы лучше, если бы вы изменили дизайн так, чтобы он не был нужен.

Вот как будет выглядеть ваш пример кода при использовании отражения. Человек, это безобразно. ;)

public class Outer {
    public static void foo(Inner inner) {
        try {
            Field this$0 = inner.getClass().getDeclaredField("this$0");
            Outer outer = (Outer) this$0.get(inner);
            System.out.println("The outer class is: " + outer);

        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public class Inner {
    }

    public void callFoo() {
        // The constructor of Inner must be called in 
        // non-static context, inside Outer.
        foo(new Inner()); 
    }

    public static void main(String[] args) {
        new Outer().callFoo();
    }
}
14 голосов
/ 18 апреля 2009

Нет, по замыслу. Если вам нужен доступ к внешнему классу через экземпляр внутреннего класса, тогда ваш дизайн обратный: точка внутреннего класса обычно используется только внутри внешнего класса или через интерфейс.

8 голосов
/ 18 апреля 2009

Что не так с добавлением геттера, когда вам нужен доступ к внешнему классу? Таким образом, вы можете контролировать, разрешен ли доступ.

6 голосов
/ 14 июля 2012

Это, на самом деле, очень хороший вопрос, если, например, вам необходимо проверить, имеют ли два разных экземпляра класса Innner один и тот же экземпляр класса Outer (== или равно в зависимости от в контексте).

Я бы предложил создать интерфейс общего назначения (не обязательный для именованных внутренних классов, но может быть "приведен к экземпляру")

public interface InnerClass<Outer> {
    Outer getOuter();
}

, который может быть применен к любому именованному внутреннему классу.

Тогда вы делаете что-то вроде:

class MyInnerClass implements InnerClass<Outer> {
    Outer getOuter() {
        return Outer.this;
    }
    // remaining implementation details
}

и таким образом вы можете получить доступ к внешнему классу из любого внутреннего класса, реализующего интерфейс InnerClass<Outer> (и проверить, действительно ли он его реализует).

Если ваш внутренний класс анонимный, вы можете это сделать только (спасибо Rich MacDonald за образец):

new InterfaceOrAbstractClass<Outer>() {
    Outer getOuter() { // super inefficient but this is the only way !
        return (Outer)getClass().getDeclaredField("this$0");
    }
    /* other methods */
}

но InterfaceOrAbstractClass должен реализует InnerClass<Outer>, чтобы иметь возможность доступа к getOuter() вне тела анонимного класса!

Было бы намного проще, если бы javac автоматически реализовывал некоторый интерфейс InnerClass<Outer> во ВСЕХ внутренних классах, и это могло бы сделать это очень эффективно даже на анонимных классах (без медленной обработки интроспекции)!

1 голос
/ 29 января 2013

В чем проблема просто написать это?

class OuterClass{
class InnerClass{
    OuterClass outerClass = OuterClass.this;
}

}

1 голос
/ 04 ноября 2009

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

Для записи работает "inner.getClass (). GetDeclaredField (" this $ 0 ")". Поскольку поле является закрытым, вам также необходимо вызвать field.setAccessible (true).

1 голос
/ 18 апреля 2009

Если у вас есть (нестатический) внутренний класс, то вы по определению работаете с чем-то, что функционирует только внутри окружающего контекста внешнего класса. Таким образом, чтобы получить дескриптор внутреннего класса, вы уже должны были получить его через внешний экземпляр. Таким образом, единственный способ добраться до точки, где вам понадобится такой аксессор, - это захватить внутреннее через внешний реф, а затем потерять или отбросить ссылку на внешний экземпляр.

Например:

public class Outer{ 
   public class Inner {
   }
}

public class UsesInner{
 Collection<Outer.Inner> c = new ArrayList<Outer.Inner>();
}

Теперь единственный способ заполнить c - это создать экземпляры Outer (). Мне было бы любопытно увидеть полезную цель для чего-то подобного.

0 голосов
/ 18 апреля 2009

Разве вы не можете просто сделать что-то вроде этого:

public static class Inner { // static now
    private final Outer parent;

    public Inner(Outer parent) { this.parent = parent; }

    public Outer get Outer() { return parent; }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...