Java - 抽象类、equals() 和两个子类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2698419/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 22:24:05  来源:igfitidea点击:

Java - abstract class, equals(), and two subclasses

javaabstract-classsubclassequalsabstract

提问by msr

I have an abstract class named Xptoand two subclasses that extend it named Personand Car. I have also a class named Testwith main() and a method foo()that verifies if two persons or cars (or any object of a class that extends Xpto) are equals. Thus, I redefined equals()in both Person and Car classes. Two persons are equal when they have the same name and two cars are equal when they have the same registration.

我有一个名为Xpto的抽象类和两个扩展它的子类,名为PersonCar。我还有一个名为Test的类,带有 main() 和一个方法foo(),用于验证两个人或汽车(或扩展 Xpto 的类的任何对象)是否相等。因此,我在 Person 和 Car 类中重新定义了equals()。两个人同名时相等,两辆车同名时相等。

However, when I call foo() in the Test class I always get "false". I understand why: the equals() is not redefined in Xpto abstract class. So... how can I compare two persons or cars (or any object of a class that extends Xpto) in that foo() method?

但是,当我在 Test 类中调用 foo() 时,我总是得到“false”。我明白为什么:equals() 没有在 Xpto 抽象类中重新定义。那么...如何在 foo() 方法中比较两个人或汽车(或扩展 Xpto 的类的任何对象)?

In summary, this is the code I have:

总之,这是我的代码:

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);
        }
}

回答by pakore

As the others say, the signature of the method you override must be exactly the same. When overriding methods, to make sure you are overriding, use the @Overrideannotation above the function, so IDEs like Eclipse will warn you if you changed the method.

正如其他人所说,您覆盖的方法的签名必须完全相同。覆盖方法时,为确保覆盖,请使用@Override函数上方的注释,这样 Eclipse 等 IDE 会在您更改方法时警告您。

This is what it would look like:

这是它的样子:

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

I would suggest to override hashCode()as well because when inserting items into lists, sets, hastables, etc... for equality (and performande) hashCode()is used (and sometimes equals()is not!)

我也建议覆盖hashCode(),因为在将项目插入列表、集合、hastables 等时......使用了相等(和性能)hashCode()(有时equals()不是!)

So your final code would be:

所以你的最终代码是:

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

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

More info at the javadoc

javadoc 中的更多信息

回答by Michael Borgwardt

I understand why: the equals() is not redefined in Xpto abstract class.

我明白为什么:equals() 没有在 Xpto 抽象类中重新定义。

Actually equals()isn't redefined anywherein your code. To override it, your method has to have Objectas parameter type and you have to cast it (after testing with instanceofto return falsewhen instances of two different subclasses are compared).

实际上equals()没有在您的代码中的任何地方重新定义。要覆盖它,您的方法必须具有Object作为参数类型,并且您必须对其进行转换(在比较两个不同子类的实例时instanceof返回测试后false)。

回答by Maurice Perry

declaring public boolean equals(Person p) or public boolean equals(Car p) does not override Object's public boolean equals(Object o), it's just a new method that is never called.

声明 public boolean equals(Person p) 或 public boolean equals(Car p) 不会覆盖 Object 的 public boolean equals(Object o),它只是一个从未被调用的新方法。

回答by Matthew Wilson

Don't you need public boolean equals(Object o)as the method signature in both of your classes?

您不需要public boolean equals(Object o)作为两个类中的方法签名吗?

回答by Daff

The Javadocstates that you need to override the equals method with object as a parameter.

Javadoc中指出,你需要重写equals方法与对象作为参数。

Indicates whether some other objectis "equal to" this one.

指示其他某个对象是否“等于”这个对象

Therefore your subclasses equals methods should look something like this:

因此,您的子类 equals 方法应如下所示:

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;
    }
}

回答by Yishai

It is generally very difficult/impossible to fully fulfill the equals contract and still have two different classes in the hierarchy equal to each other, and it is generally not done. Generally an equals method tests for the class being the same (so two instances of the same subclass will equal each other, but two instances of two different subclasses will not).

通常很难/不可能完全履行equals契约并且仍然在层次结构中具有彼此相等的两个不同的类,并且通常不会这样做。通常,equals 方法测试类是否相同(因此同一子类的两个实例将彼此相等,但两个不同子类的两个实例不会)。

However, in your case it is possible to implement an equals in Xpto since there is only one property. The obvious way to do this is to define an abstract method in Xpto, and then override equals in Xpto as well:

但是,在您的情况下,可以在 Xpto 中实现 equals,因为只有一个属性。显而易见的方法是在 Xpto 中定义一个抽象方法,然后在 Xpto 中也覆盖 equals:

 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();
        }
  }

Others have pointed out that you did not actually override equals in your implementation. In the future you can get the compiler to help you out with that by using the @Override annotation. In your case you would have gotten a compile error early which would have saved you some time.

其他人指出您实际上并没有在您的实现中覆盖 equals。将来,您可以使用 @Override 注释让编译器帮助您解决这个问题。在您的情况下,您会很早就收到编译错误,这会为您节省一些时间。

回答by JRL

Your equals method should look like:

您的 equals 方法应如下所示:

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

Also, don't forget to override hashCodeas well, to be able to properly use your types in collections.

另外,不要忘记覆盖hashCode,以便能够在集合中正确使用您的类型。

回答by fish

You are not overriding the equals()method, instead you overload it. Change the signature to

您没有覆盖该equals()方法,而是重载了它。将签名更改为

public boolean equals(Object o)

And then cast o to Person/Car and do the comparison.

然后将 o 转换为 Person/Car 并进行比较。

And BTW, you could compare the strings with equals()as well:

顺便说一句,您也可以将字符串与以下内容进行比较equals()

return registration.equals(car.registration);

回答by cHao

Your subclasses are defining equals(Person) or equals(Car), neither of which is going to like being passed an Xpto. If you declare them both as equals(Xpto), or better yet equals(Object) so that they'll work in collections, then your problem should go away.

您的子类定义了 equals(Person) 或 equals(Car),它们都不会喜欢传递 Xpto。如果您将它们都声明为 equals(Xpto) 或更好的 equals(Object) 以便它们可以在集合中工作,那么您的问题应该会消失。

Note, if you redeclare the equals() methods this way, (1) you'll need to check the classes of the objects you get passed, since you can't guarantee they're Cars or Persons anymore, and (2) you'll probably want to override getHashCode() as well, especially if you decide to make them both equals(Object), cause getHashCode() should return equal hash codes for two objects that are equal.

请注意,如果您以这种方式重新声明 equals() 方法,(1) 您需要检查传递的对象的类,因为您不能再保证它们是 Cars 或 Persons,并且 (2) 您' 可能也想覆盖 getHashCode(),特别是如果你决定让它们都等于(Object),因为 getHashCode() 应该为两个相等的对象返回相等的哈希码。

回答by Itay Maman

Here's how I would go about it:

这是我将如何去做:

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);
    }
}

Every class inherit an equals(Object)method from the Objectclass. Thus, Xptodoes not need to define such a method.

每个类都equals(Object)从该类继承一个方法Object。因此,Xpto不需要定义这样的方法。

When one overrides this method in subclasses (namely: Person, Car) one must define it with the exact same signature. In other words, the parameter of the equalsmethod must be of type Object, and the method implementation must downcast it.

当在子类(即:Person, Car)中覆盖此方法时,必须使用完全相同的签名定义它。换句话说,equals方法的参数必须是 type Object,并且方法实现必须向下转换它。