2009-10-23 15 views
47

Tengo un grupo de personas que implementan una interfaz común: Comando.Obligar a una clase a anular el método .equals

Y este grupo de clase va a un mapa.

Para que el Mapa funcione correctamente, necesito que cada clase que implementa Comando sobrescriba el método Object.equals(Object other).

está bien.

Pero me gustaría forzar la anulación de iguales. => Tiene un error de compilación cuando algo que implementa comando no anula iguales.

¿Es eso posible?

Editar: Por cierto, yo también tenga que forzar la anulación del código hash ...

Respuesta

74

No, no puede. Lo que puede hacer, sin embargo, es el uso de una clase base abstracta en lugar de una interfaz, y hacer equals() abstracta:

abstract class Command { 
    // put other methods from Command interface here 

    public abstract boolean equals(Object other); 
    public abstract int hashCode(); 
} 

Las subclases de Command obligada a continuación proporcionan sus propios métodos equals y hashCode.

En general, es una mala práctica obligar a los usuarios de la API a extender una clase base, pero puede estar justificado en este caso. Además, si convierte Command en una clase base abstracta en lugar de una interfaz, en lugar de introducir una clase base artificial en además de en la interfaz de Comando, no hay riesgo de que los usuarios de la API se equivoquen.

+14

Es posible que desee agregar un resumen 'hashCode', solo para completarlo. – McDowell

+0

Muy bien ... editado en consecuencia – skaffman

+0

funcionan como un amuleto, y de hecho, además ya tenía una clase base abstracta ... por lo tanto, no es una gran refactorización. Gracias –

0

Desde equals() se hereda de Object Dudo realmente no se puede obligar a que, ya que para cada tipo hay una implementación heredada automática de equals() disponible.

17

¿Puede ampliar sus objetos desde un XObject abstracto en lugar de java.lang.Object?

public abstract class XObject 
extends Object 
{ 
@Override 
public abstract boolean equals(Object o); 
} 
+2

Estoy sorprendido, pero esto realmente funciona. – Jorn

+1

Sin embargo, no hay garantía de que las implementaciones de Command extiendan esta clase. – skaffman

+0

Esto era lo que iba a publicar originalmente :-) –

1

Esto solo sería posible si Command fuera una interfaz, o una clase abstracta, donde equals (..) es un método declarado como abstracto.

El problema es que Object, que es la superclase de todos los objetos, ya define este método.

Si desea indicar que se trata de un problema (en tiempo de ejecución), puede lanzar una excepción, para forzar a los usuarios de su API a que lo sobrescriban. Pero no es posible en tiempo de compilación, al menos que yo sepa.

Trate de evitarlo, teniendo un método específico de API, p. Ej. CommandEquals. La otra opción es (como se mencionó) extender otra clase que define un método abstracto Equals.

+0

Solo funciona para clases abstractas (como describe Pierre), no para interfaces. No tiene que implementar el método equals declarado en la interfaz, porque ya está presente en su objeto. – Jorn

+0

Lee mi segunda oración nuevamente. –

0

No creo que sea posible forzar la anulación de iguales ya que proviene de la clase Object.

En una nota relacionada, tenga en cuenta que debe anular el método 'hashCode' de la clase Object siempre que anule equals. Esto adquiere especial importancia si va a utilizar instancias de sus clases como claves de un Mapa.Compruebe este artículo: http://www.artima.com/lejava/articles/equality.html que proporciona algunas pistas sobre cómo anular es igual manera correcta

0

Como las otras respuestas ya han explicado, no, no se puede obligar a la clase de cosa que usted está tratando.

Una cosa que podría funcionar "suficiente" es definir una segunda interfaz, llamar a este MappableCommand.

public interface MappableCommand 
{ 

} 

En la documentación para esta interfaz indicar que una clase sólo debe implementar esta interfaz (vacío) si el diseñador de la clase ha considerado que los requisitos establecidos.

Luego puede establecer el Tipo de valor de su mapa en MappableCommand, y solo MappableCommands se podrán agregar al mapa.

Esto es similar a la lógica detrás de por qué uno necesita implementar la interfaz (en blanco) Serializable para las clases que pueden ser serializadas por los mecanismos de serialización predeterminados de Java.

Si esto no funciona, entonces puede que tenga que conformarse con arrojar un error de tiempo de ejecución;

Editar falso:

Si quería hacer este requisito más evidente se podría definir la nueva interfaz de esta manera

public interface MappableCommand 
{ 

    public void iOverrodeTheEqualsMethod(); 

    public void seriouslyIPromiseThatIOverrodeIt(); 

} 
0

Puede crear boolean myEquals() en interface Command, y crear adaptador de la siguiente manera:

class MyAdapter{ 
    Command c; 
    boolean equals(Object x) { 
    return c.myEquals((Command)x); 
    } 
} 

Entonces usted simplemente usa map.put(key, new MyAdapter(command)) en lugar de map.put(key, command)

0

Aquí hay una variación de algunas de las otras soluciones propuestas:

public abstract class CommandOverridingEquals implements Command { 
    public abstract boolean equals(Object other); 
    public abstract int hashcode(); 
} 

Map<String, CommandOverridingEquals> map = 
    new HashMap<String, CommandOverridingEquals>(); 

O si realmente quiere estar seguro, utilice un HashMap comprobado; p.ej.

Map<String, CommandOverridingEquals> map = Collections.checkedMap(
    new HashMap<String, Command>(), 
    String.class, CommandOverridingEquals.class); 

Pero no importa lo que hagas no se puede detener a alguien haciendo esto:

public class AntiFascistCommand extends CommandOverridingEquals { 
    public boolean equals(Object other) { return super.equals(other); } 
    public int hashcode() { return super.hashcode(); } 
    ... 
} 

tiendo a pensar que este tipo de cosas va a causar problemas en la pista. Por ejemplo, supongamos que tengo un montón de clases de comando existentes que amplían alguna otra clase base, y (por cierto) anulan equals y hashcode de la manera prescrita. El problema es que no puedo usar esas clases. En cambio, me veo obligado a volver a implementarlos o a escribir un montón de envoltorios.

IMO, es una mala idea tratar de forzar al desarrollador a un patrón de implementación particular. Sería mejor poner algunas advertencias fuertes en los Javadocs y confiar en los desarrolladores para hacer lo correcto.

+0

Claro, pero si el desarrollador se pierde la anulación de .equals. Será un desastre en varios mapas. Y difícil de entender. Me arriesgo –

1
interface A{ 
    public boolean equal2(Object obj); 
} 

abstract class B implements A { 

    @Override 
    public boolean equals(Object obj) { 
     return equal2(obj); 
    } 

} 


class C extends B { 

    public boolean equal2(Object obj) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 
} 
+0

así que C debe reemplazar un método usado para iguales. –

1

Bueno, si quieres un tiempo de ejecución de verificación se puede hacer algo como:

interface Foo{ 

} 
class A implements Foo { 

} 
class B implements Foo { 
    @Override 
    public boolean equals(Object obj) { 
     return super.equals(obj); 
    } 
} 

public static void main(String[] args) { 
    Class<A> clazzA = A.class; 
    Class<B> clazzB = B.class; 

    Class<Object> objectClass = Object.class; 
    try { 
     Method methodFromObject = objectClass.getMethod("equals",Object.class); 
     Method methodFromA = clazzA.getMethod("equals",Object.class); 
     Method methodFromB = clazzB.getMethod("equals",Object.class); 
     System.out.println("Object == A" + methodFromObject.equals(methodFromA)); 
     System.out.println("Object == B" + methodFromObject.equals(methodFromB)); 
    } catch (SecurityException e) { 
     e.printStackTrace(); 
    } catch (NoSuchMethodException e) { 
     e.printStackTrace(); 
    } 
} 

imprimirá cierto para la primera y falsa para el segundo. Si lo desea, el tiempo de compilación parece que la única opción es crear una anotación y utilizar la herramienta de procesamiento de anotaciones para comprobar que todas las clases anotadas anulan iguales.

0

¿Es posible que nos brinde su propio java.util.comparator en el mapa en cuestión?

Cuestiones relacionadas