2009-04-18 5 views
0

J'ai une classe B de Java avec une classe interne C. Certaines méthodes de B acceptent une instance de C comme paramètre mais je veux seulement accepter les instances C créées par l'instance appropriée de B. Y at-il un moyen de faire cette validation à la compilation?Validation des instances de classes internes


Exemple

C c1 = new C(); 
B foo = c1.getB(); // foo was created by instance c1 
C c2 = new C(); 
c2.method(foo); // I want a compiler error here. 

Mon cas

ont souvent des noms de classe Carte qui détiennent une matrice d'instances de la MapArea de classe interne. La bonne chose à propos de ce schéma est que je peux valider les champs xPos et yPos du constructeur afin de ne pas créer de zones non valides pour une carte donnée. La carte en tant que méthode distanceFrom (MapArea startingPos, MapArea toLocation, MapArea ... otherLocations) et j'essayais d'éviter de valider à nouveau les arguments de la zone de carte.

+0

C c1 = new C(); B foo = c1.getB() C c2 = nouveau C(); c2.method (toto) // Je veux une erreur de compilation ici. –

+0

Donc vous parlez d'avoir plusieurs objets externes (B) qui peuvent chacun créer plusieurs objets internes (C), mais certaines méthodes d'objet C doivent seulement accepter le B à partir duquel ils ont été créés? Je ne vois pas comment vous allez faire ça au moment de la compilation. Vous parlez plus de la hiérarchie d'exécution. La seule façon dont je peux voir que vous pouvez le faire est de marquer chaque C avec son parent (le B qui l'a créé) et de le valider d'une manière ou d'une autre à l'exécution avec un assert. Le compilateur ne peut probablement pas vous aider. – Andrew

+1

En regardant plus à votre exemple: réponse courte, non. Le compilateur ne peut pas valider comme vous le souhaitez. – Andrew

Répondre

0

Si vous rendez le constructeur de la classe interne (C) privé, je crois que la classe englobante (B) peut toujours l'instancier alors que les autres classes ne le peuvent pas. Cela garantit que seuls B et C peuvent instancier C.

Editer: J'ai vérifié cela avec une petite maquette. Rendez le constructeur de la classe interne private, puis seulement la classe interne (C) ou la classe englobante (B) peut l'instancier.

Voir http://tns-www.lcs.mit.edu/manuals/java-1.1.1/guide/innerclasses/spec/innerclasses.doc6.html pour plus d'informations. En particulier: "La protection d'accès n'empêche jamais une classe d'utiliser un membre d'une autre classe, tant que l'un enferme l'autre, ou qu'ils sont entourés d'une troisième classe.".

+0

J'ai déjà un constructeur privé, vous voudrez peut-être vérifier l'exemple que j'ai ajouté. –

2

Il n'y a aucun moyen (AFAIK) de faire cela au moment de la compilation.

Lors de l'exécution, vous pouvez le faire en faisant passer la méthode d'usine de l'instance externe par une référence à elle-même au constructeur de l'instance interne.

La classe interne aurait besoin de stocker cette référence, telle que la classe externe peut vérifier si elle a créé cette instance ou non:

public class C { 

    public class B { 
     private C parent; 
     private B(C parent) { 
      this.parent = parent; 
     } 
     public C getParent() { 
      return parent; 
     } 
    } 

    public B getB() { 
     return new B(this); 
    } 

    public void method(B b) { 
     assert(this == b.getParent()); 
    } 
} 

En fait, comme le montre la réponse simultanées de Kip, B peut accéder C.this pour obtenir l'objet parent de sorte qu'il n'est pas nécessaire de stocker la référence parente. Cependant, la méthode ci-dessus serait nécessaire si C n'était pas réellement une classe interne.

2

Une erreur de compilation ne fonctionnera pas, mais vous pouvez au moins jeter une exception:

public class C 
{ 
    public static void main (String [] args) 
    { 
    C c1 = new C(); 
    B b = c1.getB(); 
    c1.useB(b); //OK 
    C c2 = new C(); 
    c2.useB(b); //throws IllegalArgumentException 
    } 

    public B getB() { return new B(); } 

    public void useB(B b) { 
    if(b.getC() != this) 
     throw new IllegalArgumentException(); 
    //... 
    } 

    private class B 
    { 
    public C getC() { return C.this; } 
    //... 
    } 
} 
0

Il n'y a aucun moyen de compilation pour se prémunir contre l'utilisation spécifique à l'instance. Votre meilleur pari est probablement de lancer une exception lorsque l'utilisation est incorrecte. Une autre option que vous avez est d'avoir la classe parent pour avoir Map des instances de la classe interne, et d'avoir d'autres classes dire à la classe externe d'opérer sur la classe interne non pas par l'instance mais par d'autres références. Cela fonctionnera avec d'autres classes qui n'ont rien à faire directement avec la classe interne.

3

Si c'est vraiment le comportement que vous voulez, method() devrait vraiment être défini dans la classe interne.

En d'autres termes, au lieu de:

public class C { 
    //... 
    public void method(B b) { 
    this.x = b.y; 
    //... 
    } 
    //... 
    public class B { 
    //... 
    } 
    //... 
} 

Il devrait être:

public class C { 
    //... 
    public class B { 
    //... 
    public void method() { 
     C c = this.C; 
     c.x = this.y; 
     //... 
    } 
    //... 
    } 
    //... 
} 

Bien sûr, cela ne résoudrait pas le problème si, par exemple, que vous vouliez public void method(B b1, B b2, B b3), où tout trois instances de B sont entourées par la même instance de C.

+1

+1. C'est la meilleure réponse que j'ai vue jusqu'ici. Si la méthode est sur la classe interne, il n'est pas possible d'invoquer la mauvaise classe externe. – Eddie