Pregunta

Tengo un montón de clases que implementan una interfaz común: Comando.

Y este grupo de clases va a un mapa.

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

está bien.

Pero me gustaría forzar la anulación de iguales. = > Se produce un error de compilación cuando algo que implementa el comando no reemplaza a los iguales.

¿Es eso posible?

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

¿Fue útil?

Solución

No, no puedes. Sin embargo, lo que puede hacer es utilizar una clase base abstracta en lugar de una interfaz y hacer que equals () abstract:

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

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

Las subclases de Command must luego deben proporcionar sus propios métodos equals y hashCode.

En general, es una mala práctica obligar a los usuarios de API a extender una clase base, pero en este caso puede justificarse. Además, si convierte a Command en una clase base abstracta en lugar de una interfaz, en lugar de introducir una clase base artificial en adición a la interfaz de Command, entonces no hay riesgo de que su API los usuarios lo entienden mal.

Otros consejos

¿Puedes extender tus objetos desde un XObject abstracto en lugar de java.lang.Object?

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

Las clases abstractas no funcionarán si tienes un nieto, ya que su padre ya reemplazó los métodos igual y hashCode y luego tienes tu problema nuevamente.

Intente usar anotaciones y APT ( http: // docs.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html ) para hacerlo.

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 el objeto, que es la superclase de todos los objetos, ya define este método.

Si desea indicar que esto es un problema (en tiempo de ejecución), podría lanzar una excepción para forzar a los usuarios de su API a anularla. Pero no es posible en tiempo de compilación, al menos que yo sepa.

Intente evitarlo, teniendo un método específico de API, por ejemplo, CommandEquals. La otra opción es (como se mencionó) extender otra clase que define un método abstracto de Igualdad.

Puedes crear boolean myEquals () en interface Command , y crear un Adaptador como este:

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

Luego simplemente usa map.put (clave, nuevo MyAdapter (comando)) en lugar de map.put (clave, comando)

Bueno, si quieres una verificación de tiempo de ejecución, puedes 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();
    }
}

Se imprimirá verdadero para el primero y falso 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 verificar que todas las anulaciones anuladas sean iguales.

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.");
    }
}

Dado que equals () se hereda de Object , dudo que realmente no pueda forzar eso, ya que para cada hay una automática implementación heredada de equals () disponible.

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 cuando alguna vez anule iguales. Esto es especialmente importante si va a usar instancias de sus clases como claves de un Mapa. Revisa este artículo: http://www.artima.com/lejava/articles/equality.html que proporciona algunas sugerencias sobre cómo anular iguales de la manera correcta

Como ya se ha explicado en las otras respuestas, no, no puedes forzar el tipo de cosa que estás tratando de hacer.

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

public interface MappableCommand 
{

}

En la documentación de esta interfaz, indique que una clase solo debe implementar esta interfaz (vacía) si el diseñador de la clase ha considerado los requisitos que usted indicó.

Luego, puede establecer el tipo de valor de su mapa en MappableCommand, y solo MappableCommands podrá agregarse al mapa.

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

Si esto no funciona, es posible que tenga que conformarse con lanzar un error de tiempo de ejecución;

Edición falsa:

Si quisiera que este requisito fuera más obvio, podría definir la nueva interfaz de esta manera

public interface MappableCommand 
{

    public void iOverrodeTheEqualsMethod();

    public void seriouslyIPromiseThatIOverrodeIt();

}

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 quieres estar seguro, usa un mapa hash comprobado; por ejemplo

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

Pero no importa lo que hagas, no puedes evitar que alguien haga 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 causará problemas en el futuro. Por ejemplo, supongamos que tengo un montón de clases de comando existentes que extienden alguna otra clase base, y (de manera incidental) invalida es igual a y hashcode de la manera prescrita. El problema es que no puedo usar esas clases. En su lugar, me veo obligado a volver a implementarlos o escribir un montón de envoltorios.

OMI, es una mala idea intentar forzar al desarrollador a un patrón de implementación particular. Sería mejor poner algunas advertencias firmes en los Javadocs y confiar en que los desarrolladores hagan lo correcto .

¿Es posible que proporcione su propio java.util.comparator al mapa en cuestión?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top