Чтобы узнать, какой из Item
логически соответствует некоторым другим Item
для рушится, я взял на себя ответственность за то, что сам класс Item
. Вы можете переопределить метод equals
, но если вы захотите положить их в Set
, это может привести к нежелательным результатам, поэтому лучше всего использовать отдельный метод проверки.
Другой вариант - взять те поля, которые будут использоваться для этой проверки, и превратить их в внутренний класс Item
. equals
и hashCode
могут быть переопределены только для внутреннего класса, а его экземпляры используются как ключ для Map
.
Ничего из этого не будет автоматически включать любые новые поля, которые вы добавляете позже. Таким образом, каждый, кто поддерживает класс, должен удостовериться, что все элементы, которые должны быть включены в проверку (или equals/hashCode), добавляются к методу (методам).
Единственный способ, которым я действительно могу придумать, чтобы приблизиться к этому, - использовать отражение. Если что-то, что должно быть учтено, входит только в внутренний класс, это сработает. Если вы должны держать его непосредственно в классе Item
, возможно, было бы полезно определить аннотацию (с сохранением времени исполнения). Код, выполняющий проверку (или equals/hashCode, если используется), может отражать класс и использовать каждое аннотированное поле.
аннотаций может выглядеть примерно так:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CollapseField {
}
А затем используется так:
class Item {
@CollapseField
String type;
@CollapseField
boolean flag;
int size;
...
}
код, используя это было тогда необходимо проверить, какие поля аннотированный и получить их значения (оба эти действия с использованием отражения) и проверить равенство с другими объектами, чтобы установить, которые принадлежат друг другу. Поскольку это может сильно повлиять на производительность, использование кэширования для чего-то вроде хеш-кода было бы хорошей идеей.
В конце концов, я не уверен, стоит ли это по жесткому кодированию используемых значений, если вы не собираетесь использовать это в большом количестве классов или количество полей может стать довольно большим.
Наконец, это может показаться странным для Java, но, возможно, использование шаблона свойств вместо полей может иметь смысл. Хотя вы потеряете некоторую безопасность. Стив Йегге сделал длинный, но интересный пост: http://steve-yegge.blogspot.be/2008/10/universal-design-pattern.html
Это почти все, что я могу придумать с головы. Насколько я знаю, стандартного подхода нет. Возможно, кто-то знает какую-то удобную библиотеку, предлагающую решение.
EDIT: Вот пример, где поля, которые будут использоваться для ключа сделаны во внутренний класс, который реализует equals
и hashCode
, поэтому он может быть использован в качестве ключа для Map
:
import java.util.Objects;
public class Item {
int size;
final Key key;
public class Key {
String type;
boolean flag;
public Key(String type, boolean flag) {
this.type = type;
this.flag = flag;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public int hashCode() {
int hash = 5;
hash = 89 * hash + Objects.hashCode(this.type);
hash = 89 * hash + (this.flag ? 1 : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Key other = (Key) obj;
if (!Objects.equals(this.type, other.type)) {
return false;
}
if (this.flag != other.flag) {
return false;
}
return true;
}
}
public Item(String type, boolean flag, int size) {
key = new Key(type, flag);
this.size = size;
}
public String getType() {
return key.type;
}
public void setType(String type) {
this.key.type = type;
}
public boolean isFlag() {
return key.flag;
}
public void setFlag(boolean flag) {
this.key.flag = flag;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public Key getKey() {
return key;
}
}
Получатели и сеттеры на уровне Item
передают некоторые из полей на Key
. Обратите внимание, что получатели и сеттеры в Key
могут не понадобиться, если вы только проходите через Item
, так как поля напрямую доступны для содержащего класса. Если вам нужно добавить поле, которое должно быть частью ключа, добавьте его в Key
. Если он не может использоваться для идентификации, добавьте его непосредственно в Item
. equals
и hashCode
могут быть легко сгенерированы любым приемлемым IDE, если вы должны их обновить.
Обратите внимание, что это решение может сломаться, если вы используете класс в некоторой структуре, которая делает отражение или интроспекцию. В зависимости от того, как он приближается, поля в Key
могут в конечном итоге рассматриваться как свойства Item
(из-за геттеров/сеттеров) или нет. Что-то вроде JPA или контейнера EJB, приближающегося к полям непосредственно через отражение, может не сработать с этим.
мммм, это выглядит более инженерии, особенно отражающая часть – AdamSkywalker
Это Java для вас, мой друг. В отсутствие более мощных языков и средств метапрограммирования появляются странные перегруженные шаблоны. Именно поэтому существует такое смехотворное количество фабрик и абстрактных фабрик, и кто знает, что еще все в Java API и фреймворках. –
О, и прежде, чем я забуду, определенно главная причина для такого огромного количества шаблонов. Возможно, загляните в Project Lombok, чтобы сделать вещи менее болезненными. Я не уверен, что он поможет вам в этом деле, но может (легко генерировать equals/hashCode с одной аннотацией). –