英文:
Comparing objects with equals
问题
Sure, here is the translated part:
我有一个任务需要在Java中比较两个对象。一个是具有天、月和年的Date
,第二个是它的扩展,带有小时和分钟的DateT
。为了实现这一目标:
- 我不能使用
Objects
类中的任何函数。 - 我也不能在一个类中使用另一个类的名称。
- 但我可以使用
instanceof
。
问题在于,当我比较Date date
和Date 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
。相反,应该参考像LocalDate
和YearMonth
这样实现常见接口但不是彼此子类的例子。
但由于这是一个作业,以下是两种解决方法:
问题在于重写的方法在第一个对象上调用,没有考虑到第二个对象可能是子类。因此,将比较操作拆分为两个方法,并在第二个对象上调用第二个方法:
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
时存在轻微冗余,因为传递给sameDate
的this
已知是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 && ((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;
}
}
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 < 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);
}
}
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
实现的对称和传递要求。
您需要确保只比较Date
与Date
和DateT
与DateT
。因此,让它们在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;
}
但由于DateT
是Date
的子类型,所以请将这个内容放入您的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 "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;
}
But since DateT
is a subtype of Date
, put this in your Date
class
@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;
}
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
- Check the argument is not
null
. Anull
can be passed into any derived data type variable. - Return
false
if the argument is not the Canonical Name doesn't match with your class name (I'm guessing it isjava.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() != "java.util.Date")
return false;
else {
Date d = (Date) other;
return day == d.day && month == d.month && 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 类的 hours 和 minutes 都为 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
&& months == date.months
&& 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
&& months == date.months
&& days == date.days
&& hours == date.hours
&& minutes == date.minutes;
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论