toString (), equals () и hashCode () в интерфейсе
Итак, у меня есть интерфейс с кучей методов, которые должны быть реализованы, имена методов не имеют никакого отношения.
объекты, реализующие этот интерфейс, часто помещаются в коллекции, а также имеют специальный формат toString (), который я хочу, чтобы они использовали.
Итак, я подумал, что было бы удобно поместить hashCode (), equals () и toString() в интерфейс, чтобы убедиться, что я не забыл переопределить метод по умолчанию для них. Но когда я добавил Эти методы для интерфейса IDE / компилятор не жалуется, если у меня нет этих трех методов, даже если я явно помещаю их в интерфейс.
Почему это не будет применяться для меня? Он жалуется, если я не реализую ни один из других методов, но он не применяет эти три. Что это дает? Какие-нибудь улики?
12 ответов:
все объекты в Java наследуются от
java.lang.Objectи объект предоставляет реализации этих методов по умолчанию.если ваш интерфейс содержит другие методы, Java будет жаловаться, если вы не реализуете интерфейс полностью, предоставляя реализацию этих методов. Но в случае
equals(),hashCode()иtoString()(как и несколько других, которые вы не упомянули) реализация уже существует.один из способов вы могли бы сделать то, что вы хотите указать другой метод в интерфейсе, скажем,
toPrettyString()или что-то подобное. Тогда вы можете вызвать этот метод вместо значения по умолчаниюtoString()метод.
похоже, что вы хотите силу ваши классы для переопределения реализаций по умолчанию этих методов. Если это так, то способ сделать это-объявить абстрактный суперкласс, который имеет методы, объявленные как абстрактные. Например:
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.Objectsaftisfy ограничение.JLS 8.1.5 указано следующее:
" если объявляемый класс не является абстрактным, все абстрактные методы-члены каждого прямого суперинтерфейса должны быть реализованы (§8.4.8.1) либо объявлением в этом классе или по существующему объявлению метода, унаследованному от прямого суперкласса или прямого суперинтерфейса, потому что класс то, что не является абстрактным, не допускается иметь абстрактные методы (§8.1.1.1)."
все 3 из этих методов определяются
java.lang.Objectкоторый (неявно) расширен всеми другими классами; поэтому реализации по умолчанию для этих методов существуют, и компилятору не на что жаловаться.
любой класс, который реализует интерфейс также расширяет объект. Объект определяет hashCode, equals и toString и имеет реализации по умолчанию всех трех.
то, что вы пытаетесь достичь, хорошо, но не осуществимо.
Если вы хотите переопределить Equals() и hashCode(), исходящие от абстрактного суперкласса, который определяет эти методы как абстрактные.
ваш объект уже содержит реализации этих трех методов, потому что каждый объект наследует эти методы от объекта, если они не переопределены.
Java заботится только о том, что методы определены где-то. Интерфейс не заставляет вас переопределять методы в новых классах, которые наследуются от интерфейса в первый раз, если они уже определены. Так как
java.lang.Objectуже реализует эти методы, ваши новые объекты соответствуют интерфейсу, даже если они не переопределяют эти три метода самостоятельно.
другие люди ответили на ваш фактический вопрос достаточно. Что касается решения вашей конкретной проблемы, вы можете подумать о создании своих собственных методов (возможно, getStringRepresentation, getCustomHashcode и equalsObject), и ваши объекты расширяют базовый класс, чьи методы equals, toString и hashCode вызывают эти методы.
Это может победить цель использования интерфейса в первую очередь, хотя. Это одна из причин, что некоторые люди имеют предположил, что equals, toString и hashCode никогда не должны были быть включены в класс объектов в первую очередь.
Адам дает вам причину, по которой вы не можете уйти с попыткой заставить 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(); } }теперь любой класс, который требует, чтобы он определенно отличался и представлялся как строка, может расширить 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() { ... } }
абстрактные классы не будут работать, если у вас есть внук, так как его отец уже переопределил методы equals и hashCode, а затем у вас снова возникла проблема.
попробуйте использовать аннотатины и APT (http://docs.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html), чтобы сделать это.
ну, если u объявили интерфейс, в котором по умолчанию все методы абстрактны, и u нужно предоставить функциональность, но когда u реализует ее в подклассе, тогда u предоставляет право реализации. как вы можете видеть, что каждый класс является подклассом одного суперкласса(проще говоря объект суперкласса всех классов) поэтому, если у вас есть класс, реализующий интерфейс, вам нужно предоставить реализацию для методов. но здесь нужно помнить одну вещь что.
независимо от того, если u не объявлял эти методы в интерфейсе, у вас все еще было это поведение для подкласса, который реализовал интерфейс в первую очередь.
Так что если не объявить его, он все равно будет присутствовать и другое дело, что, поскольку эти методы и другие методы класса Object для всех объектов классов, поэтому нет необходимости для реализации.
Comments