比较对象使用 equals 方法。

huangapple go评论63阅读模式
英文:

Comparing objects with equals

问题

Sure, here is the translated part:

我有一个任务需要在Java中比较两个对象。一个是具有天、月和年的Date,第二个是它的扩展,带有小时和分钟的DateT。为了实现这一目标:

  • 不能使用Objects类中的任何函数。
  • 我也不能在一个类中使用另一个类的名称。
  • 但我可以使用instanceof

问题在于,当我比较Date dateDate dateT(子类对象)时,我需要返回false

问题是,date.equals(dateT)返回true。你有什么建议吗?

public boolean equals(Object other) 
    if (!(other instanceof Date))
        return false;
    Date d = (Date) other;
    return day == d.day && month == d.month && year == d.year;
英文:

I have a task to compare between two objects in java. One is a Date that has days, months and years. the second one extends it, DateT with hours and minutes. In order to to that:

  • I can't use any function from Objects Class.
  • I also can't use the Name of one Class in the other Class.
  • But I can use instanceof.

The problem is that when I compare between Date date and Date dateT(the subclass object) I need to return false.

The problem is that date.equals(dateT) returns true. Do you have some suggestions?

public boolean equals(Object other) 
    if (!(other instanceof Date))
        return false;
    Date d = (Date) other;
    return day == d.day && month == d.month && year == d.year;

答案1

得分: 1

以下是翻译好的内容:

答案应该是你不应该以那种方式设计你的软件。不要参考坏的例子,比如Timestamp以不兼容的方式扩展了Date。相反,应该参考像LocalDateYearMonth这样实现常见接口但不是彼此子类的例子。

但由于这是一个作业,以下是两种解决方法:

问题在于重写的方法在第一个对象上调用,没有考虑到第二个对象可能是子类。因此,将比较操作拆分为两个方法,并在第二个对象上调用第二个方法:

class Date {
    

    @Override
    public boolean equals(Object obj) {
        return obj instanceof Date && ((Date)obj).sameDate(this);
    }

    protected boolean sameDate(Date d) {
        return day == d.day && month == d.month && year == d.year;
    }
}
class DateT extends Date {
    

    @Override
    public boolean equals(Object obj) {
        return obj instanceof DateT && ((DateT)obj).sameDate(this);
    }
  
    @Override
    protected boolean sameDate(Date d) {
        return d instanceof DateT && month == d.month && year == d.year;
    }
}

可重写的方法equals将委托给方法参数的可重写方法,因此如果this或参数对象是DateT,至少会调用一个重写的方法,从而强制要求两个对象必须都是DateT才能相等。

当两个对象都是DateT时存在轻微冗余,因为传递给sameDatethis已知是DateT类型,但为了避免这种检查,比较逻辑必须提取到第三个方法中,这在这里不值得努力。

另一种方法是考虑,即使DateT不使用day字段,每个实例都会继承自Day。因此,用一个永远不会等于有效Day实例的值来初始化它,问题就解决了。例如:

public class Date {
    final int day, month, year;

    public Date(int day, int month, int year) {
        if(day < 0) {
            throw new IllegalArgumentException("day = " + day);
        }
        this.day = day;
        this.month = month;
        this.year = year;
    }

    // only for subclasses, might be even more restricted than protected,
    // package-private or even private when using nested classes
    protected Date(int month, int year) {
        this.day = -1;
        this.month = month;
        this.year = year;
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof Date d
            && day == d.day && month == d.month && year == d.year;
    }
}

public class DateT extends Date {
    public DateT(int month, int year) {
        super(month, year);
    }
}

这假设一个日子永远不会是负数,并在构造函数中强制执行此属性。只有特殊的构造函数,应该只能被子类访问,将日子初始化为-1,这永远不会匹配Date实例的有效日子。这样,Date实例永远不等于DateT实例,无需在equals方法中进行特殊处理。

如果有可变的day字段,必须采取额外的措施来确保没有方法可以更改这些不变性。

英文:

The answer should be that you shouldn’t design your software that way. Don’t look at bad examples, like Timestamp extending Date in an incompatible manner. Rather, look at examples like LocalDate and YearMonth, which implement common interfaces but are not subclasses of each other.


But since this is an assignment, here are two ways to solve this:

The problem is that the overridden method is invoked on the first object, which does not take into account that the second object might be a subclass. Therefore, split the comparison operation into two methods and invoke the second method on the second object:

class Date {
    

    @Override
    public boolean equals(Object obj) {
        return obj instanceof Date &amp;&amp; ((Date)obj).sameDate(this);
    }

    protected boolean sameDate(Date d) {
        return day == d.day &amp;&amp; month == d.month &amp;&amp; year == d.year;
    }
}
class DateT extends Date {
    

    @Override
    public boolean equals(Object obj) {
        return obj instanceof DateT &amp;&amp; ((DateT)obj).sameDate(this);
    }
  
    @Override
    protected boolean sameDate(Date d) {
        return d instanceof DateT &amp;&amp; month == d.month &amp;&amp; year == d.year;
    }
}

The overridable method equals delegates to the overridable method of the method argument, so if either this or the argument object is a DateT, at least one overridden method will be invoked, enforcing that both objects must be DateT to be equal.

There’s a small redundancy when both objects are DateT, as the this passed to sameDate is already known to be of type DateT but to avoid this check, the comparison logic would have to be extracted into a third method which is not worth the effort here.


The alternative is to consider that even if DateT doesn’t use the day field, each instance will have it, inherited from Day. So, initialize it with a value that can never be equal to a valid value of a Day instance and you’re done. E.g.

public class Date {
    final int day, month, year;

    public Date(int day, int month, int year) {
        if(day &lt; 0) {
            throw new IllegalArgumentException(&quot;day = &quot; + day);
        }
        this.day = day;
        this.month = month;
        this.year = year;
    }

    // only for subclasses, might be even more restricted than protected,
    // package-private or even private when using nested classes
    protected Date(int month, int year) {
        this.day = -1;
        this.month = month;
        this.year = year;
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof Date d
            &amp;&amp; day == d.day &amp;&amp; month == d.month &amp;&amp; year == d.year;
    }
}

public class DateT extends Date {
    public DateT(int month, int year) {
        super(month, year);
    }
}

This assumes that a day can never be negative and enforces this property in the constructor. Only the special constructor, which should only be accessible to subclasses, will initialize the day to -1 which can never match a valid day of a Date instance. This way, Date instances are never equal to DateT instances without the need for a special handling in the equals method.

If you have a mutable day field, additional measures must be taken to ensure that no method can change these invariants.

答案2

得分: 0

假设我理解了问题,我相信以下内容应该能够强制执行您的equals实现的对称和传递要求。

您需要确保只比较DateDateDateTDateT。因此,让它们在toString()中分别返回它们自己的类名称,并检查它是否与传递的对象的toString()匹配。这仅在Date类中需要。

以下内容应该满足您的要求,因为没有Date*类提到其他类。

所以在您的DateT类中,添加

@Override
public String toString() { 
    return "DateT";
}

public boolean equals(Object date) {
    if (this == date) { // 同一对象
        return true;
    }
    if (date instanceof DateT d) { // 现在将d强制转换为dateT
        return <使用DateT值进行比较>;
    }
    return false;
}

但由于DateTDate的子类型,所以请将这个内容放入您的Date类中

@Override
public String toString() {
    return "Date";
}

public boolean equals(Object date) {
    if (this == date) { // 同一对象
        return true;
    }
    if ((date instanceof Date d) && date.toString().equals("Date")) {
        return <使用Date值进行比较>;
    }
    return false;
}

注意:自Java 14起,可以使用隐式转换,如下所示:

public boolean equals(Object foo) {
    if (foo instanceOf Foo f) { // 如果为真,f现在是Foo的一个实例,可以直接使用。
       // ...
    }
}
英文:

Assuming I understand the issue, I believe the following should enforce the symmetric and transitive requirements of your equals implementations.

You need to make sure that you only properly compare Date to Date and DateT to DateT. So have them each return their own class name in toString() and check to see that it matches the passed object's toString(). This is only needed in the Date class.

The following should meet your requirements as no Date* class mentions the other.

So in your DateT class, put

@Override
pubic String toString() { 
    return &quot;DateT&quot;;
}
public boolean equals(Object date) {
        if (this == date) { // same object
           return true;
        if (date instanceof DateT d) { // d is now cast as dateT
            return &lt;comparison using DateT values&gt;
        }
        return false;
}

But since DateT is a subtype of Date, put this in your Date class

@Override
public String toString() {
    return &quot;Date&quot;;
}

public boolean equals( Object date) {
  if( this == date) {  // same object
     return true;
  }
  if ((date instanceof Date d) &amp;&amp; date.toString().equals(&quot;Date&quot;)) {
        return &lt;comparison using Date values&gt;
  }
  return false;
}

Note: As of Java 14 implied casting can be used as follows:

public boolean equals(Object foo) {
  if (foo instanceOf Foo f) { // if true f is now an instance of Foo and
                                  // can be used directly.
   ...
} 

</details>



# 答案3
**得分**: 0

这一行代码 `if (!(other instanceof Date))` 在你将 `DateT` 类型作为参数传递给 `equals` 方法时,将始终为假,因为 `DateT` 是 `Date` 的子类。因此,`return false;` 不会被执行,而会检查自定义比较。

在进行自定义比较之前,您需要执行两件事情:

1. 检查参数是否不是 `null`。`null` 可以传递给任何派生的数据类型变量。
2. 如果参数的规范名称与您的类名不匹配(我猜它是 `java.util.Date`),则返回 `false`。

这是如何检查相等性的方式:

```java
public boolean equals(Object date) {
    
    if (date == null)
        return false;
    
    else if (!date.getClass().getCanonicalName().equals("java.util.Date"))
        return false;
    
    else {
        Date d = (Date) date;
        return day == d.day && month == d.month && year == d.year;
    }
}
英文:

This line if (!(other instanceof Date)) will always be false when you supply DateT type as the argument to equals method because DateT is a subclass of Date. Thus the return false; will not be executed and the custom comparison will be checked.

You need to do two things before your custom comparison

  1. Check the argument is not null. A null can be passed into any derived data type variable.
  2. Return false if the argument is not the Canonical Name doesn't match with your class name (I'm guessing it is java.util.Date).

This is how the equality should be checked.

public boolean equals(Object date) {

	if(date == null)
		return false;
	
	else if(date.getClass().getCanonicalName() != &quot;java.util.Date&quot;)
		return false;
	
	else {
		Date d = (Date) other;
	    return day == d.day &amp;&amp; month == d.month &amp;&amp; year == d.year;
	}
}

答案4

得分: 0

为了使用 DateT 类来否定比较,您可以首先检查 obj 参数是否是 DateT 的实例。

此外,在此处,您可以在 instanceof 语句中使用 模式变量

class Date {
    int years, months, days;

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof DateT) return false;
        if (obj instanceof Date date) {
            return years == date.years
                && months == date.months
                && days == date.days;
        }
        return false;
    }
}

子类 equals 可以有两种实现方式,我不确定哪种更有效。从本质上讲,如果 DateT 类的 hoursminutes 都为 0,它 可能 是等效的。

在这种情况下,DateT 不等于 Date

class DateT extends Date {
    int hours, minutes;

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof DateT date)) return false;
        return years == date.years
            && months == date.months
            && days == date.days
            && hours == date.hours
            && minutes == date.minutes;
    }
}
英文:

To negate the comparison using the DateT class, you can first check if the obj parameter is an instance of DateT.

Additionally, here you can use a pattern variable within the instanceof statement.

class Date {
    int years, months, days;

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof DateT) return false;
        if (obj instanceof Date date) {
            return years == date.years
                &amp;&amp; months == date.months
                &amp;&amp; days == date.days;
        }
        return false;
    }
}

The sub-class equals can be implemented in two ways, I'm not sure which is more valid.
Essentially, it could be equivalent if the hours and minutes of the DateT class are 0.

In this case, a DateT will not equal a Date.

class DateT extends Date {
    int hours, minutes;

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof DateT date)) return false;
        return years == date.years
            &amp;&amp; months == date.months
            &amp;&amp; days == date.days
            &amp;&amp; hours == date.hours
            &amp;&amp; minutes == date.minutes;
    }
}

huangapple
  • 本文由 发表于 2023年5月29日 19:33:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76356971.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定