为实现相同接口的两个子类定义泛型方法

Cod*_*ein 11 java generics set

我正在用Java构建一个"联系人管理器".

我有一个名为" Contact的超类,它有两个基类; PersonalContactBusinessContact.

我有一个名为Event的接口,它由类BirthdayMeeting实现.(生日包含一个DateTime对象,而会议有两个开始和结束时间).

PersonalContact拥有一个生日树组,BusinessContact拥有一组会议.

现在,在超类Contact中,我想创建一个名为"getEventsWithinPeriod()"的抽象方法,它将在给定的时间跨度内返回所有生日和/或会议的TreeSet.

问题是,我不知道如何告诉抽象方法,然后基类方法返回什么.

例如,这是我在Contact中使用的代码;

public abstract Set<Event> getEventsWithinPeriod(DateTime start, DateTime end);

在PersonalContact中;

public Set<Birthday> getEventsWithinPeriod(DateTime start, DateTime end){

      Set<Birthday> birthdaysThatAreWithin = new TreeSet<Birthday>();
      //CODE
      return birthdaysThatAreWithin;
Run Code Online (Sandbox Code Playgroud)

但是,在编译器中,我说错了Set<Birthday> ;

"返回类型与Contact.getEventsWithinPeriod(DateTime,DateTime)不兼容"

我应该使用的正确条款和回报是什么?为什么我目前的尝试错了?

Ami*_*nde 10

你需要使用 generic Types

public abstract class Contact<T extends Event> {
    public abstract Set<T> getEventsWithinPeriod(Date start, Date end);
}
public class BirthDay extends Contact<BirthDay> implements Event {

    @Override
    public Set<BirthDay> getEventsWithinPeriod(Date start, Date end) {
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)


Bri*_*ian 6

你有3个解决方案.

解决方案1

首先,您可以使您的类具有通用性,如下所示:

public abstract class Contact<E extends Event> {
    // ...

    public abstract Set<E> getEventsWithinPeriod(DateTime start, DateTime end);
}
Run Code Online (Sandbox Code Playgroud)

然后在您的具体实现中:

public class PersonalContact extends Contact<Birthday> {

    public Set<Birthday> getEventsWithinPeriod(DateTime start, DateTime end) { ... }
}
Run Code Online (Sandbox Code Playgroud)

这是最好的解决方案,但您有一些替代方案.

解决方案2

您可以更改birthdaysThatAreWithin字段的类型:

Set<Event> birthdaysThatAreWithin = new TreeSet<Event>();
Run Code Online (Sandbox Code Playgroud)

以及更改方法签名:

public Set<Event> getEventsWithinPeriod(DateTime start, DateTime end) {
Run Code Online (Sandbox Code Playgroud)

然后就这样回归 这限制了您,因为您不能Birthday再将事件用作实例.

解决方案3

您还可以将方法签名(在抽象类和具体类中)更改为:

public Set<? extends Event> getEventsWithinPeriod(DateTime start, DateTime end)
Run Code Online (Sandbox Code Playgroud)

而不是改变任何其他东西.这与解决方案2具有相同的问题,您将无法将事件作为Birthday实例使用而不进行转换.

编辑: 2和3的缺点是他们需要投射.例如:

PersonalContact contact = ... ;
Set<Event> events = personalContact.getEventsWithinPeriod(start, end);
// I know all the events are birthdays, but I still have to do this:
for (Event event : events) {
    if (event instanceof Birthday) {
        Birthday birthday = (Birthday) event;
        // Do stuff with birthday
    } // else maybe log some error or something
}
Run Code Online (Sandbox Code Playgroud)

有了第一个解决方案,你就有了这个:

PersonalContact contact = ... ;
Set<Birthday> birthdays = personalContact.getEventsWithinPeriod(start, end);
for (Birthday birthday : birthdays) {
    // Do stuff with birthday
}
Run Code Online (Sandbox Code Playgroud)

代码看起来更干净,运行得更好,因为您不必进行instanceof检查以确保您没有得到ClassCastException.你也可以这样的东西:

public static void processBirthdaysFor(Contact<Birthday> birthdayContact, DateTime start, DateTime end) {
    Set<Birthday> birthdays = personalContact.getEventsWithinPeriod(start, end);
    for (Birthday birthday : birthdays) {
        // Do stuff with birthday
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您有另一个Contact具有Birthday事件的实现,您可以将它们传递给该processBirthdaysFor方法而不进行任何更改.

但是,如果您只需要这些事件并且您不关心调用您的代码中的类型Contact.getEventsWithinPeriod,那么解决方案2和3 绝对是您最好的选择.如果是这种情况,我个人只会使用解决方案2.