2009-04-15 5 views
5

Je pense que ce qui suit ne peut pas être fait en Java. Mais je serais heureux d'apprendre à mettre en œuvre quelque chose qui lui ressemble. Supposons que nous ayons une classe C, déjà utilisée dans le code compilé.mettre en œuvre des interfaces après le fait

(Nous ne pouvons pas changer ce code ni la définition originale de C). Supposons en outre qu'il y ait un code intéressant qui pourrait être réutilisé, si seulement C mettait en oeuvre l'interface I. Il est, en fait, plus ou moins trivial de dériver D c'est juste C + l'implémentation des méthodes d'interface.

Pourtant, il semble qu'il n'y a aucun moyen, une fois que j'ai un C, pour dire: Je veux que vous soyez un D, ​​qui est une C mise en œuvre I.

(remarque Side: Je pense que le casting (D) c, où le type d'exécution de C est C, devrait être autorisé si D est un C et la seule différence avec C sont des méthodes ajoutées.Cela devrait être sûr, n'est-ce pas?)

Comment pourrait-on contourner ce problème? calamité?

(Je connais le modèle de conception d'usine, mais ce n'est pas une solution, car une fois que nous réussissons à créer D dans tous les endroits où C étaient auparavant, quelqu'un d'autre trouve une autre interface J utile et dérive E C implémente J. Mais E et D sont incompatibles, puisqu'ils ajoutent tous deux un ensemble de méthodes différent de C. Donc, bien que nous puissions toujours passer un E où C est attendu, nous ne pouvons pas passer un E où D est attendu. On aurait plutôt besoin d'une nouvelle classe F extends C implements I, J.)

Répondre

7

Si tout ce que vous devez être compatible avec les interfaces S alors aucun problème Jetez un oeil à dynamic proxy classes, c'est fondamentalement comment vous implémentez des interfaces à l'exécution dans Java.

Si vous avez besoin d'une compatibilité d'exécution similaire avec les classes, je vous suggère de consulter les bibliothèques open source cglib ou javaassist.

+0

Cela semble assez complexe, mais je vais certainement essayer. Pouvez-vous dire quelque chose sur le coût d'exécution, c'est-à-dire que j'ai besoin d'une instance de gestionnaire d'invocations supplémentaire par objet. – Ingo

+1

La méthode déléguée serait beaucoup plus simple, ceci ajoute de la complexité sans aucun bénéfice pour ce problème particulier. – Robin

+0

ma pensée était qu'une autre solution était nécessaire autre que l'application du modèle d'adaptateur spécifiquement une solution d'exécution. peut-être une mauvaise interprétation – MahdeTo

10

Impossible d'utiliser une classe déléguée, c'est-à-dire une nouvelle classe qui enveloppe une instance de "Classe C", mais implémente aussi "Interface I"?

public class D implements I { 

    private C c; 

    public D (C _c) { 
     this.c = _c; 
    } 

    public void method_from_class_C() { 
     c.method_from_class_C(); 
    } 
    // repeat ad-nauseum for all of class C's public methods 
    ... 

    public void method_from_interface_I() { 
     // does stuff 
    } 
    // and do the same for all of interface I's methods too 
} 

puis, si vous avez besoin d'invoquer une fonction qui prend normalement un paramètre de type I faire ceci:

result = some_function(new D(c)); 
+0

pourquoi pas simplement "la classe D étend C implémente I"? – dfa

+0

car il requiert soit que vous construisiez avec 'new D (...)' au lieu de 'new C (...)', ce qui peut ne pas être possible. Il n'est pas possible non plus de faire "D d = (D) c" même si D étend C. Les downcasts de D à C devraient être possibles, cependant. – Alnitak

+0

J'ai essayé de l'expliquer auparavant. Je reçois des C du code qui ne sont pas sous mon contrôle et je veux les passer (pas un nouvel objet/différent) au code qui fonctionne avec les interfaces. – Ingo

3

Si vous (pouvez) gérer le ClassLoader qui charge votre classe C alors vous pouvez essayer de faire des shenanigans de temps de chargement de classe avec l'instrumentation bytecode pour que la classe implémente l'interface.

La même chose peut être faite pendant la construction, bien sûr. Cela pourrait même être plus facile de cette façon (car vous n'avez pas besoin d'accéder au ClassLoader).

1

Je crois que ce que vous voulez est possible en utilisant java.lang.reflect.Proxy; en fait, j'ai fait quelque chose de similaire pour un projet en cours. Cependant, c'est un peu de travail, et les «objets hybrides» qui en résultent peuvent exposer un comportement étrange (parce que les appels de méthodes sont routés vers différents objets concrets, il y a des problèmes quand ces méthodes essayent de s'appeler).

2

(remarque Side: Je pense que la distribution (D) c, où type d'exécution de c est C, devrait être autorisé si D est un C et la seule différence C sont des méthodes ajouté Cela devrait. soyez en sécurité, n'est-ce pas?

Pas du tout. Si vous pouviez faire cette distribution, vous pourriez compiler du code qui tentait d'appeler l'une des "méthodes ajoutées" sur cet objet, qui échouerait à l'exécution puisque cette méthode n'existe pas en C.

Je pense que vous imaginez que la distribution détecterait les méthodes "manquantes" de C et les déléguerait automatiquement à D. Je doute que ce soit faisable, bien que je ne puisse pas parler des implications de la conception linguistique.

Il me semble la solution à votre problème est:

Définir la classe D, qui étend C et met en œuvre I
Définir un constructeur D (C c) qui clone essentiellement l'état de l'objet C donné en un nouvel objet D.
L'objet D peut être passé à votre code existant car il s'agit d'un C, et il peut être passé au code qui veut un I parce que c'est un I

+0

Je voulais dire (D) c changer réellement le type d'exécution de c en D (en écrasant simplement le vtable-pointeur, par exemple). Bien sûr, cela ne devrait être possible que si le compilateur peut prouver qu'un cast de C en D ne change pas le comportement Cish de l'objet, mais ajoute simplement de nouvelles fonctionnalités. – Ingo

+0

Normalement, il est seulement possible de rétrograder (c'est-à-dire d'une sous-classe à sa super-classe) parce que cela supprime _ la fonctionnalité et les membres de données. – Alnitak

0

Je pense que vous ne pouvez pas le faire parce que Java est strictement typé. Je crois que cela peut être fait dans des langages comme Ruby et Python avec une utilisation de mixins. Comme pour Java, cela ressemble certainement à un bon usage pour le modèle de conception de l'adaptateur (il a déjà été proposé plus tôt comme un objet "wrapper").