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


我正在用Java构建一个“联系人管理器”。

我有一个名为“的超类”联系 它有两个基类; 个人联系 和 BusinessContact

我有一个名为的界面 事件,由类实现 生日 和 会议。 (生日包含一个DateTime对象,而会议有两个开始和结束时间)。

个人联系 拥有一个TreeSet 生日 和 BusinessContact 拥有一套 会议。

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

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

例如,这是我使用的代码 联系;

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;

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

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

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


3675
2017-11-02 16:02


起源

为什么你不想继续使用 Event 接口?接口的主要思想是通过定义可见的实现方法列表来使代码清晰。只是用 TreeSet<Event> - Maxim Shoustin
我完全同意,@ Fess - 似乎很清楚“getEvents ...”会返回事件,不是吗?在这种情况下使用泛型只是令人困惑和无益。 - Bill K
是的,你们是对的,我最终这样做了。我没有清楚地了解如何使用接口将两个类统一为“事件”横幅下的相同类型。谢谢你的帮助! - CodyBugstein


答案:


你有3个解决方案。

解决方案1

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

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

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

然后在您的具体实现中:

public class PersonalContact extends Contact<Birthday> {

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

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

解决方案2

你可以改变你的类型 birthdaysThatAreWithin 领域:

Set<Event> birthdaysThatAreWithin = new TreeSet<Event>();

以及更改方法签名:

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

然后就这样回归这会限制您,因为您无法将事件用作 Birthday 实例了。

解决方案3

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

public Set<? extends Event> getEventsWithinPeriod(DateTime start, DateTime end)

而不是改变任何其他东西。这与解决方案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
}

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

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

代码看起来更干净,运行得更好,因为您不必这样做 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
    }
}

如果你有另一个实现 Contact 具有 Birthday 事件,你可以传递给它 processBirthdaysFor 方法没有做任何改变。

然而,如果你只需要这些事件而你不关心调用你的代码中的类型是什么 Contact.getEventsWithinPeriod,然后解决方案2和3 无疑 你最好的赌注。如果是这种情况,我个人只会使用解决方案2。


6
2017-11-02 16:12



谢谢,我认为解决方案2会很棒。我的问题是,实际上是什么缺点?什么意思是我将无法将事件用作生日实例?我失去了什么? - CodyBugstein
@Imray我会更新我的答案 - Brian
@Imray答案更新,看一看。 - Brian
很棒的解释 - 非常感谢! - CodyBugstein


你需要使用 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;
    }
}

10
2017-11-02 16:07



我不知道是否 <T> 是好习惯。因为 T 应该是一切,它就像默认界面。也许 Contact<T extends Event> - Maxim Shoustin
@Fess它已经更新了。请检查 - Amit Deshpande
哦,好的,谢谢 - Maxim Shoustin
但这意味着在使用Contact的所有其他类和方法中(例如在我的ContactManager类中包含Contacts的TreeSet),我必须将其更改为Contact <T extends Event>?所以我将有TreeSet <Contact <T extends Event >> ?? - CodyBugstein
@Imray你可以用 TreeSet<Contact<? extends Event>> 如果有这样的特定要求。如果您使用任何其他approch,此方法将删除将需要的类型转换 - Amit Deshpande


在覆盖任何方法时,方法签名应保持相同,您的签名应保持相同并在PersonalContact类中返回Set


0
2017-11-02 16:09





使用泛型时,您不希望显式指定类型。您可以绑定类型,但您不希望显式。

改变你的 Contact 方法

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

并改变 PersonalContact 至

public Set<T extends Event> getEventsWithinPeriod(DateTime start, DateTime end){

      Set<T> birthdaysThatAreWithin = new TreeSet<Birthday>();
      //CODE
      return birthdaysThatAreWithin;
}

那应该得到你想要的东西。


0
2017-11-02 16:10



我不需要将Contact类更改为Contact <T extends Event>然后吗? - CodyBugstein