Подтвердить что ты не робот

ToString(), equals() и hashCode() в интерфейсе

Итак, у меня есть интерфейс с кучей методов, которые нужно реализовать, имена методов не имеют значения.

Объекты, реализующие этот интерфейс, часто помещаются в коллекции, а также имеют специальный формат toString(), который я хочу использовать.

Итак, я думал, что было бы удобно помещать hashCode(), equals() и toString() в интерфейс, чтобы я не забыл переопределить метод по умолчанию для них. Но когда я добавил эти методы в интерфейс, IDE/Compiler не жалуется, если у меня нет этих трех методов, хотя я явно помещаю их в интерфейс.

Почему это не будет принудительно для меня? Он жалуется, что я не применяю ни один из других методов, но он не применяет эти три. Что дает? Любые подсказки?

4b9b3361

Ответ 1

Все объекты в Java, наследуемые от java.lang.Object и Object, предоставляют стандартные реализации этих методов.

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

Один из способов, которым вы могли бы выполнить то, что хотите, - это предоставить другой метод в интерфейсе, например toPrettyString() или что-то в этом роде. Затем вы можете вызвать этот метод вместо метода toString() по умолчанию.

Ответ 2

Похоже, вы хотите заставить свои классы переопределять стандартные реализации этих методов. Если это так, способ сделать это - объявить абстрактный суперкласс, который имеет методы, объявленные как абстрактные. Например:

public abstract class MyBaseClass implements ... /* existing interface(s) */ {

    public abstract boolean equals(Object other);

    public abstract int hashCode();

    public abstract String toString();
}

Затем измените текущие классы на extend этот класс.

Этот подход работает, но это не идеальное решение.

  • Это может быть проблематично для существующей иерархии классов.

  • Плохая идея - заставить классы, которые реализуют ваш существующий интерфейс, расширить конкретный абстрактный класс. Например, вы можете изменять параметры в сигнатурах методов для использования абстрактного класса, а не существующего интерфейса (ов). Но конечный результат - менее гибкий код. (И люди могут найти способы повредить это в любом случае, например, добавив свой собственный абстрактный подкласс, который "реализует" методы с вызовом super.<method>(...)!)

  • Наложение определенной иерархии/реализации шаблона класса является недальновидным. Вы не можете предсказать, изменится ли какое-то будущее требование о том, что ваши ограничения вызывают трудности. (Вот почему люди рекомендуют программировать против интерфейсов, а не для определенных классов.)


Вернемся к вашему фактическому вопросу о том, почему ваш интерфейс не заставляет класс повторно использовать эти методы:

Почему это не будет принудительно для меня? Он жалуется, что я не применяю ни один из других методов, но он не применяет эти три. Что дает? Любые подсказки?

Интерфейс накладывает ограничение на то, что конкретный класс, реализующий его, имеет реализацию для каждого из методов. Однако он не требует, чтобы сам класс реализовывал эти методы. Реализации метода могут быть унаследованы от суперкласса. И в этом случае это то, что происходит. Методы, унаследованные от java.lang.Object, устраняют ограничение.

JLS 8.1.5 утверждает следующее:

"Если объявляемый класс не является абстрактным, все абстрактные методы-члены каждого прямого суперинтерфейса должны быть реализованы (§8.4.8.1) либо объявлением в этом классе , либо существующим объявлением метода, унаследованным от прямого суперкласса или прямого суперинтерфейса, потому что классу, который не является абстрактным, не разрешается иметь абстрактные методы (§8.1.1.1)."

Ответ 3

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

Ответ 4

Любой класс, реализующий ваш интерфейс, также расширяет Object. Объект определяет hashCode, equals и toString и имеет стандартные реализации всех трех.

То, что вы пытаетесь достичь, хорошо, но практически невозможно.

Ответ 5

Там реализована реализация этих методов, начиная с Object.

Ответ 6

В вашем объекте уже реализованы реализации этих трех методов, поскольку каждый объект наследует эти методы от Object, если они не переопределены.

Ответ 7

Если вы хотите принудительно переопределить equals() и hashCode(), простирайтесь от абстрактного суперкласса, который определяет эти методы как абстрактные.

Ответ 8

Java только заботится о том, чтобы методы были определены где-то. Интерфейс не заставляет вас переопределять методы в новых классах, которые наследуются от интерфейса в первый раз, если они уже определены. Поскольку java.lang.Object уже реализует эти методы, ваши новые объекты соответствуют интерфейсу, даже если они не переопределяют эти три метода самостоятельно.

Ответ 9

Другие люди правильно ответили на ваш реальный вопрос. Что касается решения вашей конкретной проблемы, вы можете подумать о создании своих собственных методов (возможно, getStringRepresentation, getCustomHashcode и equalsObject), а ваши объекты расширят базовый класс, чьи методы equals, toString и hashCode вызывают эти методы.

Это может привести к преждевременному поражению цели использования интерфейса. Это одна из причин, по которой некоторые люди предположили, что equals, toString и hashCode никогда не должны были включаться в класс Object в первую очередь.

Ответ 10

Адам дает вам причину, по которой вы не можете уйти, пытаясь заставить equals, hashCode и toString. Я бы пошел на следующую реализацию, которая затрагивает решение, данное Адамом и Стефаном:

public interface Distinct {
    boolean checkEquals(Object other);
    int hash();
}

public interface Stringable {
    String asString();
}

public abstract class DistinctStringableObject {

    @Override
    public final boolean equals(Object other) {
        return checkEquals();
    }

    @Override
    public final int hashCode() {
        return hash();
    }

    @Override
    public final String toString() {
        return asString();
    }
}

Теперь любой класс, который требует, чтобы он определенно различался и представлялся как String, может расширить объект DistinctStringableObject, который заставит реализацию checkEquals, hashCode и toString.

Пример конкретного класса:

public abstract class MyDistinctStringableObject extends DistinctStringableObject {

    @Override
    public final boolean checkEquals(Object other) {
        ...
    }

    @Override
    public final int hash() {
        ...
    }

    @Override
    public final String asString() {
        ...
    }
}

Ответ 11

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

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

Ответ 12

хорошо, если u объявил интерфейс, в котором по умолчанию все методы являются абстрактными, и вам нужно предоставить функциональность, но когда u реализует его в подклассе, тогда u обеспечит право реализации. так как вы можете видеть, что каждый класс является подклассом одного суперкласса (просто Object является суперклассом всех классов) поэтому, если у есть класс, реализующий интерфейс, вам нужно обеспечить реализацию для методов. но здесь нужно помнить что-то.

независимо от того, если u не объявлял эти методы в интерфейсе, у вас все еще было это поведение для подкласса, который реализовал интерфейс в первую очередь.

поэтому, если вы не объявите его, оно все равно будет присутствовать, а другое дело, поскольку эти методы и другие методы класса Object присутствуют для всех объектов классов, поэтому нет необходимости в реализации.