Obtention de l'objet de classe externe à partir de l'objet de classe interne
-
08-07-2019 - |
Question
J'ai le code suivant. Je souhaite mettre la main sur l'objet de classe externe à l'aide duquel j'ai créé l'objet de classe interne inner
. Comment puis-je le faire?
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: Eh bien, certains d'entre vous ont suggéré de modifier la classe interne en ajoutant une méthode:
public OuterClass outer() {
return OuterClass.this;
}
Mais que se passe-t-il si je n'ai pas le contrôle pour modifier la classe interne, alors (juste pour confirmer) avons-nous un autre moyen d'obtenir l'objet de classe externe correspondant à partir de l'objet de classe interne?
La solution
Dans la classe interne elle-même, vous pouvez utiliser OuterClass.this
. Cette expression, qui permet de faire référence à toute instance englobant lexicalement, est décrite dans le JLS comme suit: Qualifié this
.
Je ne pense pas qu’il existe un moyen de récupérer l’instance en dehors du code de la classe interne. Bien sûr, vous pouvez toujours présenter votre propre propriété:
public OuterClass getOuter() {
return OuterClass.this;
}
EDIT: Par expérimentation, il semble que le champ contenant la référence à la classe externe ait un accès au niveau du package - du moins avec le JDK que j'utilise.
EDIT: le nom utilisé ( this $ 0
) est valide en Java, bien que le JLS décourage son utilisation:
Le caractère
$
ne doit être utilisé que dans code source généré mécaniquement ou, rarement, pour accéder à des noms préexistants sur systèmes hérités.
Autres conseils
OuterClass.this
fait référence à la classe externe.
Vous pouvez (mais vous ne devriez pas) utiliser la réflexion pour le travail:
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<*>");
field.setAccessible(true);
// Dereference and cast it
Outer outer = (Outer) field.get(inner);
System.out.println(outer);
}
}
Bien sûr, le nom de la référence implicite n’est absolument pas fiable. Par conséquent, comme je l’ai dit, vous ne devriez pas: -)
La réponse plus générale à cette question concerne les variables ombrées et leur accès.
Dans l'exemple suivant (sous Oracle), la variable x dans main () est ombrée de 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();
}
}
L'exécution de ce programme imprimera:
x=0, Test.this.x=1
Plus d'informations sur: http: //docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6
Voici l'exemple:
// 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";
}
}
Je viens de le faire comme ça:
public class CherryTree {
public class Cherry {
public final CherryTree cherryTree = CherryTree.this;
// [...]
}
// [...]
}
Bien sûr, vous devez être capable de modifier la classe interne et toute personne qui obtient l’objet classe interne a désormais accès à l’objet classe externe. Dans mon cas, c'est très bien.
si vous n’avez pas le contrôle de modifier la classe interne, la réflexion peut vous aider (mais pas la recommander). Ce $ 0 est la référence de la classe Inner qui indique quelle instance de la classe Outer a été utilisée pour créer l’instance actuelle de la classe Inner.
public class Outer {
public Inner getInner(){
return new Inner(this,this.getClass());
}
class Inner {
public Inner(Outer outer, Class<? extends Outer> aClass) {
System.out.println(outer);
System.out.println(aClass.getName());
System.out.println();
}
}
public static void main(String[] args) {
new Outer().getInner();
}
}
/**
* 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<*>quot;)) {
field.setAccessible(true);
outerObj = field.get(object);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return getDeclaringTopLevelClassObject(outerObj);
}
Bien sûr, le nom de la référence implicite n'est pas fiable, vous ne devez donc pas utiliser de réflexion pour le travail.