Java - абстрактный класс, equals () и два подкласса - PullRequest
3 голосов
/ 23 апреля 2010

У меня есть абстрактный класс с именем Xpto и два подкласса, расширяющих его, с именами Person и Car .У меня также есть класс с именем Test с main () и метод foo () , который проверяет, равны ли два человека или машины (или любой объект класса, который расширяет Xpto).Таким образом, я переопределил equals () в классах Person и Car.Два человека равны, когда у них одинаковое имя, и две машины равны, если они имеют одинаковую регистрацию.

Однако, когда я вызываю foo () в классе Test, я всегда получаю «false».Я понимаю, почему: equals () не переопределен в абстрактном классе Xpto.Итак ... как я могу сравнить два человека или машины (или любой объект класса, который расширяет Xpto) в этом методе foo ()?

Итак, вот мой код:

public  abstract class Xpto {


}

public class Person extends Xpto{

        protected String name;

        public Person(String name){
                this.name = name;
        }

        public boolean equals(Person p){
                System.out.println("Person equals()?");
                return this.name.compareTo(p.name) == 0 ? true : false;
        }
}

public class Car extends Xpto{
        protected String registration;

        public Car(String registration){
                this.registration = registration;
        }

        public boolean equals(Car car){
                System.out.println("Car equals()?");
                return this.registration.compareTo(car.registration) == 0 ? true : false;
        }
}

public class Teste {

        public static void foo(Xpto xpto1, Xpto xpto2){
                if(xpto1.equals(xpto2))
                        System.out.println("xpto1.equals(xpto2) -> true");
                else
                        System.out.println("xpto1.equals(xpto2) -> false");

        }

        public static void main(String argv[]){
                Car c1 = new Car("ABC");
                Car c2 = new Car("DEF");
                Person p1 = new Person("Manel");
                Person p2 = new Person("Manel");

                foo(p1,p2);
        }
}

Ответы [ 10 ]

4 голосов
/ 23 апреля 2010

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

Вот как это будет выглядеть:

@Override
public boolean equals(Object obj){
...Your code here...
}

Я бы также предложил переопределить hashCode(), потому что при вставке элементов в списки, наборы, hastables и т. Д. ... для равенства (и performance) используется hashCode() (а иногда equals()нет!)

Таким образом, ваш окончательный код будет:

@Override
public boolean equals(Object obj){
...Your code here...
}

@Override
public int hashCode(){
...Your code here...
}

Больше информации на javadoc

2 голосов
/ 23 апреля 2010

Я понимаю, почему: равно () не переопределено в абстрактном классе Xpto.

На самом деле equals() не переопределено в любом месте в вашем коде. Чтобы переопределить его, ваш метод должен иметь Object в качестве типа параметра, и вы должны привести его к типу (после тестирования с instanceof для возврата false при сравнении экземпляров двух разных подклассов).

1 голос
/ 23 апреля 2010

Как правило, очень трудно / невозможно полностью выполнить контракт на равенство, и все же в иерархии есть два разных класса, равных друг другу, и это обычно не выполняется.Обычно метод equals проверяет, чтобы класс был одинаковым (поэтому два экземпляра одного и того же подкласса будут равны друг другу, но два экземпляра двух разных подклассов не будут).

Однако в вашем случае можнореализовать равно в Xpto, так как есть только одно свойство.Очевидный способ сделать это - определить абстрактный метод в Xpto, а затем переопределить equals в Xpto:

 public class Xpto {
        protected abstract String getIdentity();

        @Override
        public boolean equals(Object o) {
            if (o == null) return false;
            //Typical implementation
            //if (getClass() != o.getClass()) return false;
            if (!(o instanceof Xpto)) return false; //risky implementation, but will allow a car to compare to a person
             return getIdentity().equals((Xpto) o.getIdentity());
        }

        @Override
        public int hashCode() {
             return getIdentity().hashCode();
        }
  }

Другие отметили, что вы на самом деле не переопределяли equals в своей реализации.В будущем вы можете заставить компилятор помочь вам с этим, используя аннотацию @Override.В вашем случае вы получили бы ошибку компиляции раньше, что сэкономило бы вам некоторое время.

1 голос
/ 23 апреля 2010

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

@Override public boolean equals(Object o) {
   if (!(o instanceof YourType)) {
      return false;
   }
   YourType yt = (YourType)o;
   ... // rest here
}

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

1 голос
/ 23 апреля 2010

объявление публичных логических равных (Person p) или общедоступных логических равных (Car p) не отменяет общедоступных логических равных Object (Object o), это просто новый метод, который никогда не вызывается.

1 голос
/ 23 апреля 2010

Javadoc утверждает, что вам необходимо переопределить метод equals объектом в качестве параметра.

Указывает, равен ли какой-либо другой объект""this.

Поэтому ваши подклассы, равные методам, должны выглядеть примерно так:

public class Car extends Xpto
{
    protected String registration;

    public Car(String registration)
    {
        this.registration = registration;
    }

    public boolean equals(Object obj)
    {
        if (obj == null)
        {
            return false;
        }
        if (obj == this)
        {
            return true;
        }
        if (!obj.getClass().isAssignableFrom(getClass()))
        {
            return false;
        }
        Car car = (Car) obj;
        return this.registration.compareTo(car.registration) == 0 ? true : false;
    }
}
1 голос
/ 23 апреля 2010

Разве вам не нужно public boolean equals(Object o) в качестве сигнатуры метода в обоих ваших классах?

0 голосов
/ 23 апреля 2010

Ваши подклассы определяют равные (Персона) или равные (Автомобиль), ни одному из которых не понравится проходить Xpto. Если вы объявите их как равные (Xpto) или, что еще лучше, равно (Object), чтобы они работали в коллекциях, ваша проблема должна исчезнуть.

Обратите внимание: если вы переопределите методы equals () таким образом, (1) вам нужно будет проверить классы объектов, которые вы передаете, так как вы больше не можете гарантировать, что они Автомобили или Персоны, и ( 2) вы, вероятно, захотите также переопределить getHashCode (), особенно если вы решите сделать их обоих равными (Object), потому что getHashCode () должен возвращать одинаковые хеш-коды для двух одинаковых объектов.

0 голосов
/ 23 апреля 2010

Вы не переопределяете метод equals(), вместо этого вы перегружаете его.Измените подпись на

public boolean equals(Object o)

А затем выберите o Персона / Автомобиль и выполните сравнение.

И, кстати, вы также можете сравнить строки с equals():

return registration.equals(car.registration);
0 голосов
/ 23 апреля 2010

Вот как я могу это сделать:

public  abstract class Xpto {

}

public class Person extends Xpto{

    protected String name;

    public Person(String name){
            this.name = name;
    }

    public boolean equals(Object o){
       if(o == null || !getClass().equals(o.getClass())
          return false;
       Person p = (Person) o;
       System.out.println("Person equals()?");
       return this.name.compareTo(p.name) == 0 ? true : false;
    }
}

public class Car extends Xpto {
    protected String registration;

    public Car(String registration){
            this.registration = registration;
    }

    public boolean equals(Object o){
       if(o == null || !getClass().equals(o.getClass())
          return false;
       Car car = (Car) o;
       System.out.println("Car equals()?");
       return this.registration.compareTo(car.registration) == 0 ? true : false;
    }
}

public class Teste {

    public static void foo(Xpto xpto1, Xpto xpto2){
            if(xpto1.equals(xpto2))
                    System.out.println("xpto1.equals(xpto2) -> true");
            else
                    System.out.println("xpto1.equals(xpto2) -> false");

    }

    public static void main(String argv[]){
            Car c1 = new Car("ABC");
            Car c2 = new Car("DEF");
            Person p1 = new Person("Manel");
            Person p2 = new Person("Manel");

            foo(p1,p2);
    }
}

Каждый класс наследует метод equals(Object) от класса Object.Таким образом, Xpto не нужно определять такой метод.

Когда кто-то переопределяет этот метод в подклассах (а именно: Person, Car), его необходимо определить с точно такой же сигнатурой.Другими словами, параметр метода equals должен иметь тип Object, и реализация метода должна понижать его.

...