Si tengo dos interfaces, ambas bastante diferentes en sus propósitos, pero con la misma firma de método, ¿cómo hago para que una clase implemente ambas sin estar obligado a escribir un solo método que sirva para ambas interfaces y escribir alguna lógica complicada en el método? implementación que verifica para qué tipo de objeto se está realizando la llamada e invoca el código adecuado?
En C #, esto se supera mediante lo que se denomina implementación de interfaz explícita. ¿Hay alguna forma equivalente en Java?
Respuestas:
No, no hay forma de implementar el mismo método de dos formas diferentes en una clase en Java.
Eso puede llevar a muchas situaciones confusas, razón por la cual Java lo ha rechazado.
interface ISomething { void doSomething(); } interface ISomething2 { void doSomething(); } class Impl implements ISomething, ISomething2 { void doSomething() {} // There can only be one implementation of this method. }
Lo que puede hacer es componer una clase de dos clases, cada una de las cuales implementa una interfaz diferente. Entonces esa clase tendrá el comportamiento de ambas interfaces.
class CompositeClass { ISomething class1; ISomething2 class2; void doSomething1(){class1.doSomething();} void doSomething2(){class2.doSomething();} }
fuente
ISomething1 CompositeClass.asInterface1();
yISomething2 CompositeClass.asInterface2();
a esa clase. Entonces puede simplemente sacar uno u otro de la clase compuesta. Sin embargo, no existe una gran solución para este problema.public long getCountAsLong() implements interface2.getCount {...}
[en caso de que la interfaz requiera unlong
pero los usuarios de la clase esperanint
] oprivate void AddStub(T newObj) implements coolectionInterface.Add
[suponiendo quecollectionInterface
tenga uncanAdd()
método, y para todas las instancias de esta clase devuelvefalse
]?No hay una forma real de resolver esto en Java. Podría usar clases internas como solución:
interface Alfa { void m(); } interface Beta { void m(); } class AlfaBeta implements Alfa { private int value; public void m() { ++value; } // Alfa.m() public Beta asBeta() { return new Beta(){ public void m() { --value; } // Beta.m() }; } }
Aunque no permite lanzamientos de
AlfaBeta
aBeta
, los abatidos son generalmente malvados, y si se puede esperar que unaAlfa
instancia a menudo también tenga unBeta
aspecto, y por alguna razón (generalmente la optimización es la única razón válida), querrás poder para convertirlo enBeta
, usted podría hacer un sub-interfaz deAlfa
conBeta asBeta()
en ella.fuente
Si se encuentra con este problema, lo más probable es que esté usando herencia donde debería usar la delegación . Si necesita proporcionar dos interfaces diferentes, aunque similares, para el mismo modelo subyacente de datos, entonces debe usar una vista para proporcionar acceso económico a los datos utilizando alguna otra interfaz.
Para dar un ejemplo concreto para el último caso, suponga que desea implementar ambos
Collection
yMyCollection
(que no heredaCollection
y tiene una interfaz incompatible). Puede proporcionar funciones deCollection getCollectionView()
yMyCollection getMyCollectionView()
que proporcionen una implementación ligera deCollection
yMyCollection
, utilizando los mismos datos subyacentes.Para el primer caso ... suponga que realmente desea una matriz de enteros y una matriz de cadenas. En lugar de heredar de ambos
List<Integer>
yList<String>
, debe tener un miembro de tipoList<Integer>
y otro miembro de tipoList<String>
, y hacer referencia a esos miembros, en lugar de intentar heredar de ambos. Incluso si solo necesita una lista de números enteros, es mejor usar composición / delegación sobre herencia en este caso.fuente
El problema "clásico" de Java también afecta mi desarrollo de Android ...
La razón parece ser simple:
más frameworks / bibliotecas tienes que usar, más fácilmente las cosas pueden estar fuera de control ...
En mi caso, tengo una clase BootStrapperApp heredado de android.app.Application ,
mientras que la misma clase también debería implementar una interfaz de plataforma de un marco MVVM para integrarse.
La colisión de métodos ocurrió en un método getString () , que es anunciado por ambas interfaces y debe tener una implementación de red diferente en diferentes contextos.
La solución alternativa (feo..IMO) es usar una clase interna para implementar todas las plataformasmétodos, solo debido a un conflicto menor de firma de método ... en algún caso, tal método prestado ni siquiera se usa en absoluto (pero afecta la semántica de diseño principal).
Tiendo a estar de acuerdo en que la indicación explícita de contexto / espacio de nombres estilo C # es útil.
fuente
La única solución que me vino a la mente es usar objetos de referencia para el que desea implementar múltiples interfaces.
por ejemplo: suponiendo que tiene 2 interfaces para implementar
public interface Framework1Interface { void method(Object o); }
y
public interface Framework2Interface { void method(Object o); }
puede incluirlos en dos objetos Facador:
public class Facador1 implements Framework1Interface { private final ObjectToUse reference; public static Framework1Interface Create(ObjectToUse ref) { return new Facador1(ref); } private Facador1(ObjectToUse refObject) { this.reference = refObject; } @Override public boolean equals(Object obj) { if (obj instanceof Framework1Interface) { return this == obj; } else if (obj instanceof ObjectToUse) { return reference == obj; } return super.equals(obj); } @Override public void method(Object o) { reference.methodForFrameWork1(o); } }
y
public class Facador2 implements Framework2Interface { private final ObjectToUse reference; public static Framework2Interface Create(ObjectToUse ref) { return new Facador2(ref); } private Facador2(ObjectToUse refObject) { this.reference = refObject; } @Override public boolean equals(Object obj) { if (obj instanceof Framework2Interface) { return this == obj; } else if (obj instanceof ObjectToUse) { return reference == obj; } return super.equals(obj); } @Override public void method(Object o) { reference.methodForFrameWork2(o); } }
Al final, la clase que querías debería ser algo como
public class ObjectToUse { private Framework1Interface facFramework1Interface; private Framework2Interface facFramework2Interface; public ObjectToUse() { } public Framework1Interface getAsFramework1Interface() { if (facFramework1Interface == null) { facFramework1Interface = Facador1.Create(this); } return facFramework1Interface; } public Framework2Interface getAsFramework2Interface() { if (facFramework2Interface == null) { facFramework2Interface = Facador2.Create(this); } return facFramework2Interface; } public void methodForFrameWork1(Object o) { } public void methodForFrameWork2(Object o) { } }
ahora puede usar los métodos getAs * para "exponer" su clase
fuente
Puede utilizar un patrón de Adaptador para que estos funcionen. Cree dos adaptadores para cada interfaz y utilícelos. Debería resolver el problema.
fuente
Todo está bien cuando tienes control total sobre todo el código en cuestión y puedes implementarlo por adelantado. Ahora imagine que tiene una clase pública existente que se utiliza en muchos lugares con un método
public class MyClass{ private String name; MyClass(String name){ this.name = name; } public String getName(){ return name; } }
Ahora debe pasarlo al WizzBangProcessor estándar que requiere clases para implementar la WBPInterface ... que también tiene un método getName (), pero en lugar de su implementación concreta, esta interfaz espera que el método devuelva el nombre de un tipo del procesamiento de Wizz Bang.
En C # sería un truco
public class MyClass : WBPInterface{ private String name; String WBPInterface.getName(){ return "MyWizzBangProcessor"; } MyClass(String name){ this.name = name; } public String getName(){ return name; } }
En Java Tough, tendrá que identificar todos los puntos de la base de código implementado existente donde necesita convertir de una interfaz a otra. Seguro que la empresa WizzBangProcessor debería haber utilizado getWizzBangProcessName (), pero también son desarrolladores. En su contexto, getName estaba bien. En realidad, fuera de Java, la mayoría de los otros lenguajes basados en OO admiten esto. Java es raro en forzar que todas las interfaces se implementen con el mismo método NAME.
La mayoría de los otros lenguajes tienen un compilador que está más que feliz de tomar una instrucción para decir "este método en esta clase que coincide con la firma de este método en esta interfaz implementada es su implementación". Después de todo, el objetivo de definir interfaces es permitir que la definición se abstraiga de la implementación. (Ni siquiera me hagas empezar a tener métodos predeterminados en interfaces en Java, y mucho menos anulación predeterminada ... porque seguro, todos los componentes diseñados para un automóvil de carretera deberían poder chocar contra un automóvil volador y simplemente funcionar, oye Ambos son automóviles ... Estoy seguro de que la funcionalidad predeterminada de, digamos, su navegador por satélite no se verá afectada con las entradas de cabeceo y balanceo predeterminadas, ¡porque los automóviles solo giran!
fuente