Вопрос

У меня есть группа классов, которые реализуют общий интерфейс: Command.

И эта группа классов идет на карту.

Чтобы карта работала правильно, мне нужно, чтобы каждый класс, реализующий Command, переопределил метод Object.equals (Object other) .

все хорошо.

Но я хотел бы форсировать равных. = & GT; Ошибка компиляции, когда что-то, кто реализует команду, не переопределяет равно.

Это возможно?

Изменить: кстати, мне также нужно будет принудительно переопределить хэш-код ...

Это было полезно?

Решение

Нет, ты не можешь. Однако вы можете использовать абстрактный базовый класс вместо интерфейса и сделать equals () abstract:

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

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

Подклассы Command должны предоставить свои собственные методы equals и hashCode.

Обычно плохая практика заставлять пользователей API расширять базовый класс, но в этом случае это может быть оправдано. Кроме того, если вы сделаете Command абстрактным базовым классом вместо интерфейса, вместо того, чтобы вводить искусственный базовый класс в добавление в интерфейс Command, тогда нет риска для вашего API пользователи ошибаются.

Другие советы

Можете ли вы расширить свои объекты из абстрактного XObject вместо java.lang.Object?

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

Абстрактные классы не будут работать, если у вас есть внук, так как его отец уже переопределил методы equals и hashCode, и тогда у вас снова возникнет проблема.

Попробуйте использовать аннотатины и APT ( http: // docs.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html ), чтобы сделать это.

Это было бы возможно, только если Command был интерфейсом или абстрактным классом, где метод equals (..) объявлен как абстрактный.

Проблема в том, что Object, который является суперклассом всех объектов, уже определяет этот метод.

Если вы хотите указать, что это проблема (во время выполнения), вы можете вызвать исключение, чтобы заставить пользователей вашего API переопределить его. Но это невозможно во время компиляции, по крайней мере, насколько мне известно.

Попробуйте обойти это, используя специфичный для API метод, например, CommandEquals. Другой вариант (как уже упоминалось) расширяет другой класс, который определяет абстрактный метод Equals.

Вы можете создать boolean myEquals () в Интерфейсная команда и создать адаптер следующим образом:

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

Затем вы просто используете map.put (ключ, новый MyAdapter (команда)) вместо map.put (ключ, команда)

Ну, если вам нужна проверка во время выполнения, вы можете сделать что-то вроде:

    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();
    }
}

Напечатает true для первого и false для второго. Если вы хотите, чтобы время компиляции выглядело как единственный вариант, это создать аннотацию и использовать инструмент обработки аннотаций, чтобы проверить, что все аннотированные классы переопределяют равные.

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

Поскольку equals () унаследован от Object , я сомневаюсь, что вы не можете принудительно вызвать это, поскольку для каждого типа существует автоматический доступна унаследованная реализация equals () .

Я не думаю, что можно принудительно переопределить equals, как это происходит из класса Object.

Обратите внимание на то, что вам необходимо переопределить метод 'hashCode' из класса Object, когда вы переопределяете значение равно. Это становится особенно важным, если вы собираетесь использовать экземпляры ваших классов в качестве ключей карты. Проверьте эту статью: http://www.artima.com/lejava/articles/equality.html который дает некоторые советы о том, как корректно переопределить equals

Как уже объясняли другие ответы, нет, вы не можете заставить то, что вы пытаетесь сделать.

Одной вещью, которая может работать «достаточно», является определение второго интерфейса, назовите этот MappableCommand.

public interface MappableCommand 
{

}

В своей документации по этому интерфейсу укажите, что класс должен реализовывать этот (пустой) интерфейс только в том случае, если разработчик класса учел указанные вами требования.

Затем вы можете установить тип значения вашей карты на MappableCommand, и только MappableCommands смогут быть добавлены на карту.

Это похоже на логику того, почему нужно реализовать (пустой) интерфейс Serializable для классов, которые можно сериализовать с помощью механизмов сериализации Java по умолчанию.

Если это не сработает, возможно, вам придется согласиться на выдачу ошибки во время выполнения;

Поддельная правка:

Если вы хотите сделать это требование более очевидным, вы можете определить новый интерфейс таким образом

public interface MappableCommand 
{

    public void iOverrodeTheEqualsMethod();

    public void seriouslyIPromiseThatIOverrodeIt();

}

Вот вариант некоторых других предлагаемых решений:

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

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

Или, если вы действительно хотите быть уверенным, используйте проверенный hashmap; например.

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

Но независимо от того, что вы делаете, вы не можете остановить кого-то, делающего это:

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

Я склонен думать, что подобные вещи вызовут проблемы в будущем. Например, предположим, что у меня есть набор существующих классов команд, которые расширяют некоторый другой базовый класс, и (между прочим) переопределяют equals и hashcode предписанным способом. Проблема в том, что я не могу использовать эти классы. Вместо этого я вынужден переопределить их или написать кучу обёрток.

IMO, это плохая идея пытаться заставить разработчика использовать конкретный шаблон реализации. Было бы лучше поместить некоторые строгие предупреждения в Javadocs и рассчитывать на то, что разработчики поступят правильно .

Можете ли вы предоставить свой собственный java.util.comparator для рассматриваемой карты?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top