java 将对象与equals进行比较

mcdcgff0  于 2023-06-04  发布在  Java
关注(0)|答案(4)|浏览(141)

我有一个任务来比较两个java对象。一个是Date,它有日、月和年。第二个是用小时和分钟扩展它,DateT。为了做到这一点:

  • 不能使用对象类中的任何函数。
  • 我也不能在另一个类中使用一个类的名称。
  • 但是我可以使用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;
u91tlkcl

u91tlkcl1#

答案应该是你不应该那样设计你的软件。不要看糟糕的例子,比如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字段,必须采取额外的措施来确保没有方法可以改变这些不变量。

h4cxqtbf

h4cxqtbf2#

假设我理解了这个问题,我相信下面的内容应该会强制您的equals实现的对称和传递要求。
您需要确保只比较properlyDate to DateDateT to DateT。因此,让它们各自返回自己的类名toString(),并检查它是否与传递的对象的toString()匹配。这只在Date类中需要。
下面的代码应该满足您的要求,因为没有Date*类提到其他类。
因此,在DateT类中,

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

但由于DateTDate的子类型,因此将其放在Date类中

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

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

注意:从Java 14开始,隐式类型转换可以如下使用:

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

jecbmhm33#

当你提供DateT类型作为equals方法的参数时,这行if (!(other instanceof Date))将始终为false,因为DateTDate的子类。因此,return false;将不会执行,而会检查自定义比较。
在进行自定义比较之前,需要做两件事
1.检查参数是否不是nullnull可以传递到任何派生的数据类型变量中。
1.返回false,如果参数不是规范名称与类名不匹配(我猜是java.util.Date)。
这就是检查相等性的方法。

public boolean equals(Object date) {

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

qv7cva1a4#

要使用 DateT 类否定比较,可以首先检查 obj 参数是否是 DateT 的示例。
此外,这里您可以在 instanceof 语句中使用 pattern variable

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 类的 hoursminutes0,则 * 可能 * 是等效的。
在这种情况下,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;
    }
}

相关问题