Универсальные классы в java и метод equals(): безопасность типов

#java

#java

Вопрос:

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

Я добавил @SuppressWarnings («непроверенный»), чтобы «отключить» предупреждения, но проблема все еще существует. Методы GetType() и getClass() также не работают с универсальными типами, поэтому об использовании T.GetType() не может быть и речи.

 public class Pair<T, U> {
    private T first;
    private U second;

    public Pair(T _first, U _second)
    {
        first = _first;
        second = _second;
    }

    public boolean equals(Object obj) {
         if (this == obj)
           return true;
         if (obj == null)
           return false;
         if (getClass() != obj.getClass())
           return false;

        //Problem ahead!!!
         Pair<T, U> other = (Pair<T, U>) obj;

                   ...
    }
}
  

Есть ли какая-либо общая хорошая практика или «трюк», чтобы сделать это правильно и безопасно?

Комментарии:

1. Универсальные типы удаляются во время компиляции и не присутствуют во время выполнения, поэтому их «проверка» невозможна.

2. Ну, вы могли бы просто так first.equals(other.first) (если first значение не равно null) и позволить ему equals() обрабатывать проверки типов. Таким образом, если у вас есть Pair<String, String> и вы передаете Pair<Integer, Integer> , у вас будет String.equals(Integer) который вернет false.

Ответ №1:

Вы можете привести obj к Pair<?, ?> и вызвать equals first и second :

 Pair<?, ?> other = (Pair<?, ?>) obj;
return other.first.equals(first) amp;amp; other.first.equals(second);
  

Таким образом, проверки типов будут выполняться с помощью T.equals and U.equals , независимо от того, что такое T и U .

Ответ №2:

Вы могли бы написать свой equals метод следующим образом:

 @Override
public boolean equals(Object object) {
    boolean equal = false;

    if(this == object){
        equal = true;
    } else if(object instanceof Pair<?, ?>) {
        // Check that object is an instance of Pair<?, ?>, this will also null check.
        // Then just case object to Pair<?, ?> like.
        Pair<?, ?> pair = (Pair<?, ?>) object;

        if(((this.first == null amp;amp; pair.first == null) || (this.first != null amp;amp; this.first.equals(pair.first))) amp;amp;
                ((this.second == null amp;amp; pair.second == null) || (this.second != null amp;amp; this.second.equals(pair.second)))){
            equal = true;
        }
    }
    return equal;
  

? Между <> является своего рода подстановочным знаком, на самом деле он классифицируется как неограниченный подстановочный знак; это означает, что тип класса не был указан.

object instanceof Pair<?, ?> Проверит две вещи, сначала он проверит, что объект не равен null, что создает null безопасность для вас, а затем он проверит, что объект имеет тип Pair<?, ?> .

Вы можете прочитать о подстановочных знаках здесь

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

 @Override
public int hashCode() {
    final int prime = 31;
    int result = super.hashcode;

    result = result * prime   (this.first == null ? 0 : this.first.hashCode());
    result = result * prime   (this.second == null ? 0 : this.second.hashCode());

    return resu<
}
  

Почему я должен переопределять, hashCode когда я переопределяю equals ?

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

Ответ №3:

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

Также помните, что equals() это всегда существовало с первого выпуска Java, в то время как универсальные классы были введены в Java 1.5. Поэтому естественно, что для этого конкретного метода вам придется выполнить некоторое приведение типов.

После приведения объекта вы в любом случае будете проверять каждый отдельный элемент Pair , поэтому, если они другого типа, они также не пройдут проверку на равенство.

Ответ №4:

Вы можете использовать instanceof для проверки типа вашего объекта и последующего его безопасного приведения.

 if(obj instanceof Pair){
    Pair other = (Pair) obj;
}
  

Ответ №5:

Поскольку универсальный тип стирается во время компиляции, вы не можете проверить тип во время выполнения. Итак, вы должны привести его к Pair<?,?> .

Если вы используете JDK 7 или более позднюю версию, вы также можете использовать Objects.equals(..) в своем equals методе. Это упростит код, поскольку вам не нужно беспокоиться о том, является ли first или second null .

 Pair<?, ?> pair = (Pair<?, ?>) obj;
return Objects.equals(first, pair.first) amp;amp;
  Objects.equals(second, pair.second);
  

Кроме того, не забудьте реализовать, hashCode если вы добавили equals метод в свой класс.

 @Override
public int hashCode() {
  return Objects.hash(first, second);
}
  

Ответ №6:

 package com.journaldev.generics;

public class GenericsMethods {

//Java Generic Method
public static <T> boolean isEqual(GenericsType<T> g1, GenericsType<T> g2){
    return g1.get().equals(g2.get());
}

public static void main(String args[]){
    GenericsType<String> g1 = new GenericsType<>();
    g1.set("Test");

    GenericsType<String> g2 = new GenericsType<>();
    g2.set("Test");

    boolean isEqual = GenericsMethods.<String>isEqual(g1, g2);
    //above statement can be written simply as
    isEqual = GenericsMethods.isEqual(g1, g2);
    //This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets.
    //Compiler will infer the type that is needed
}
}
  

Комментарии:

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