2009-04-15 14 views
5

Creo que lo siguiente no se puede hacer en Java. Pero me encantaría aprender a implementar algo que se le parezca.implementando interfaces después del hecho

Supongamos que tenemos una clase C, que ya se utiliza en el código compilado. (No podemos cambiar ese código ni la definición original de C).

Supongamos además que hay un código interesante que podría reutilizarse, si solo C implementara la interfaz I. De hecho, es más o menos trivial derivar D que es solo C + la implementación de los métodos de interfaz.

Sin embargo, parece que no hay manera, una vez que tengo una C, para decir: Quiero que seas una D, es decir, una aplicación de C I.

(nota al pie: Creo que el molde (D) c, donde el tipo de tiempo de ejecución de c es C, debe permitirse si D es una C y la única diferencia es C. ¿Esto debería ser seguro, en caso de no hacerlo?)

¿Cómo podría funcionar esto? ¿calamidad?

(Conozco el patrón de diseño de fábrica, pero parece que no es una solución. Pues, una vez que logramos crear D's en todos los lugares donde anteriormente eran C, alguien más encuentra otra interfaz J útil y deriva E se extiende C implementa J. Pero E y D son incompatibles, ya que ambos agregan un conjunto diferente de métodos a C. Entonces, aunque siempre podemos pasar una E donde se espera una C, no podemos pasar una E donde se espera una D. Más bien, ahora, necesitaríamos una nueva clase F extiende C implementa I, J)

Respuesta

7

Si todo lo que necesita para ser compatible con es interfaces entonces no hay problema Eche un vistazo a dynamic proxy classes, básicamente cómo implementa las interfaces en tiempo de ejecución en java.

si necesita una compatibilidad de tiempo de ejecución similar con las clases, le sugiero que eche un vistazo a las bibliotecas opensource de cglib o javaassist.

+0

Esto parece bastante complejo, pero definitivamente voy a intentarlo. ¿Puede decir algo sobre el costo del tiempo de ejecución, es decir, parece que necesito una instancia de controlador de invocación adicional por objeto? – Ingo

+1

El método delegado sería mucho más simple, esto agrega complejidad sin ningún beneficio para este problema en particular. – Robin

+0

pensé que se necesitaba otra solución además de aplicar el patrón de adaptador específicamente una solución de tiempo de ejecución. tal vez una mala interpretación – MahdeTo

10

¿No podría usar una clase de delegado, es decir, una nueva clase que envuelve una instancia de "Clase C", pero también implementa "Interfaz 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 
} 

y luego, si es necesario invocar una función que normalmente toma un parámetro de tipo I acaba de hacer esto:

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

¿por qué no simplemente "clase D extiende C implementa I"? – dfa

+0

porque requiere que se construya con 'nueva D (...)' en lugar de 'nueva C (...)', lo que puede no ser posible. Tampoco es posible hacer "D d = (D) c" aunque D extienda C. Sin embargo, los Downcasts de D a C deberían ser posibles. – Alnitak

+0

Traté de explicar eso antes. Obtengo C del código que no está bajo mi control y quiero pasarlos (no un objeto nuevo/diferente) al código que funciona con las interfaces. – Ingo

3

Si puede (puede) administrar ClassLoader que carga su clase C, entonces puede intentar hacer algunos chanchullos de tiempo de carga de clases con instrumentación de bytecode para hacer que la clase implemente la interfaz.

Lo mismo se puede hacer durante el tiempo de compilación, por supuesto. Incluso podría ser más fácil de esta manera (ya que no necesita acceso al ClassLoader).

1

Creo que lo que quiere es posible usando java.lang.reflect.Proxy; de hecho, he hecho algo similar para un proyecto actual. Sin embargo, es bastante trabajo, y los "objetos híbridos" resultantes pueden exponer comportamientos extraños (porque las llamadas a métodos en ellos se enrutan a diferentes objetos concretos, hay problemas cuando esos métodos intentan llamarse entre sí).

2

(nota al margen: Creo que el molde (D) c, donde de c Tipo de tiempo de ejecución es C, debe permitido si D es un C y la única diferencia a C son métodos añadido Esto debería. estar seguro, ¿no es así?)

No, en absoluto. Si pudieras hacer este cast, podrías compilar código que intentó llamar a uno de los "métodos agregados" en este objeto, que fallaría en tiempo de ejecución ya que ese método no existe en C.

Creo que estás imaginando que el elenco detectaría los métodos que están "ausentes" de C y los delegará en D automáticamente. Dudo que sea factible, aunque no puedo hablar sobre las implicaciones del diseño del lenguaje.

Me parece la solución a su problema es:

Definir la clase D, que se extiende C e implementa I
definir un constructor D (C c) qué clones esencialmente el estado del objeto C dada en un nuevo objeto D
El objeto D se puede pasar a su código existente porque es una C, y se puede pasar al código que quiere una I porque es un I

+0

Quise decir (D) c para realmente cambiar el tipo de tiempo de ejecución de c a D (al sobrescribir el puntero vtable, por ejemplo). Por supuesto, esto solo debería ser posible si el compilador puede probar que un lanzamiento en tiempo de ejecución de C a D no cambia el comportamiento Cish del objeto, sino que simplemente agrega una nueva funcionalidad. – Ingo

+0

Normalmente, solo es posible bajar (es decir, de una subclase a su superclase) porque eso _remueve_ la funcionalidad y los miembros de datos. – Alnitak

0

Creo que no puede hacerlo porque Java es estrictamente mecanografiado. Creo que se puede hacer en idiomas como Ruby y Python con un uso de mixins.

En cuanto a Java, definitivamente parece un buen uso para el patrón de diseño del Adaptador (ya se había propuesto anteriormente como un objeto "envoltorio").

Cuestiones relacionadas