Java抽象类使用泛型实现接口


70

我试图定义一个实现Comparable的抽象类。当我用以下定义定义类时:

public abstract class MyClass implements Comparable <MyClass>

子类必须实现compareTo(MyClass object)。相反,我希望每个子类都实现compareTo(SubClass object),接受其自己类型的对象。当我尝试使用类似以下内容定义抽象类时:

public abstract class MyClass implements Comparable <? extends MyClass>

它抱怨“一个超类型可能没有指定任何通配符。”

有解决方案吗?

Answers:


50

在我看来,它有点太冗长,但可以:

public abstract class MyClass<T extends MyClass<T>> implements Comparable<T> {

}

public class SubClass extends MyClass<SubClass> {

    @Override
    public int compareTo(SubClass o) {
        // TODO Auto-generated method stub
        return 0;
    }

}

3
考虑(1)类MyImpl1扩展了MyClass <MyImpl1> {...}; (2)类MyImpl2扩展了MyClass <MyImpl1> {public int compareTo(MyImpl1 o){...}}。MyImpl2没有做正确的事。
emory

2
如果我们假设每个子类都使用自己的类作为通用参数扩展MyClass,则该解决方案是正确的。但是,正如emory指出的那样,似乎无法确保这一点。
Cem 2010年

@emory对于Comparable直接实现也是如此。没有人阻止你这样做MyImpl2 implements Comparable<MyImpl1>
whiskeysierra

1
但是声明需求会迫使子类实现可比性而不是假设它。
whiskeysierra

3
public abstract class MyClass<T> implements Comparable<T>一样好
newacct 2012年

21

除了在声明签名时遇到的机械困难之外,目标没有多大意义。您正在尝试建立协变比较函数,这打破了建立派生类可以定制的接口的整个想法。

如果您定义某个子SubClass类以使其实例只能与其他SubClass实例进行比较,那么如何SubClass满足由定义的契约MyClass?回想一下,MyClass它和它派生的任何类型都可以与其他MyClass实例进行比较。您正在努力使该不是真的SubClass,那该装置SubClass不符合MyClass的契约:你不能代替SubClassMyClass,因为SubClass的要求是严格的。

此问题集中于协方差和协方差,以及它们如何允许函数签名通过类型派生进行更改。您可以放宽对参数类型的要求(接受比父类型的签名要求宽的类型),还可以加强对返回类型的要求(保证返回比父类型的签名窄的类型)。这些自由中的每一个仍然允许将派生类型完美替换为超类型。调用者无法通过父类型的界面来区分使用派生类型时的区别,但是具体地使用派生类型的调用者可以利用这些自由。

威利(Willi)的答案教导了一些有关泛型声明的知识,但我敦促您在接受该技术之前以牺牲语义为代价重新考虑您的目标。


我同意这个答案。另外,我们应该对接口而不是类进行编码。实际的实现类可以是匿名类,本地类(嵌套在方法内部),私有(嵌套在类内部)或包私有,因此不在我们的范围之内。
emory

2
我看到的另一个问题是将子类对象存储在Collection中。我应该能够用justList<MyClass>代替List<MyClass<?>>。当您获得其中一个对象并调用时,将调用equals(anObject)什么?
TheLQ

seh,谢谢您的回答。实际上,我正在寻找一种协议,该协议将强制每个子类仅对其自己的类具有一个compareTo()方法,而对其他任何子类均不具有。从这个意义上讲,我对虚构的泛型定义可能会产生误导。
Cem 2010年

我知道,Cem,尽管强迫子类进行compareTo()定义仍然很奇怪。使用Enum,它可以为派生类型(编译器将为您生成的类型)提供该定义,这是一种不寻常的情况。就您而言,除了为派生类型提供义务外,您什么都不提供。是否要保证您可以针对类型编写泛型函数MyType<T>并确保它提供了compareTo(MyType<T>)?如果是这样,您是否也不能通过功能需求来做到这一点<T extends MyType && Comparable<T>>
SEH

3

参见Java自己的示例:

public abstract class Enum<E extends Enum<E>> implements Comparable<E>
    public final int compareTo(E o)

关于seh的评论:通常论点是正确的。但是泛型使类型关系更加复杂。在Willi的解决方案中,子类可能不是MyClass的子类型。

SubClassA是的子类型MyClass<SubClassA>,但不是的子类型MyClass<SubClassB>

类型MyClass<X>定义了compareTo(X)所有子类型都必须遵守的合同。那里没有问题。


1
这是一个很好的例子,尽管它与Cem最初的问题有所不同。现在,我可能已经从字面上读了他的代码。也许这正是他想要写的。在这种情况下,接口的Comparable方面Enum是关于特定的子类可以对其自身(或者更确切地说是其实例)进行的操作,而不是通常对Enum派生类型可以进行的操作。如果那是Cem追求的目标,那么我认为您的回答比我的回答更合适。
SEH

我想只要所有子类都是MyClass <?>的子类型就可以。不过,我不确定是否会在以后的步骤中引起问题。这是我第一次使用泛型进行设计。
Cem 2010年

这是一个不好的例子。枚举是一种特殊情况-枚举类型由语言提供,并且具有特定的形式(enum A遗嘱implement Enum<A>),这通常不适用于用户定义的类。
newacct 2012年

1

我不确定您是否需要捕获:

首先,将compareTo添加到抽象类中。

public abstract class MyClass implements Comparable <MyClass> {

@Override
public int compareTo(MyClass c) {
...
}    
}

然后添加实现...

public class MyClass1 extends MyClass {
...
}

public class MyClass2 extends MyClass {
...
}

调用compare将调用超级类型方法...

MyClass1 c1 = new MyClass1();
MyClass2 c2 = new MyClass2();

c1.compareTo(c2);

这不是Cem描述他的问题的方式吗?你将如何实现compareToMyClass1MyClass2有不同的参数类型?
whiskeysierra

1
public abstract class MyClass<T> implements Comparable<T> {

}

public class SubClass extends MyClass<SubClass> {

    @Override
    public int compareTo(SubClass o) {
        // TODO Auto-generated method stub
        return 0;
    }

}

这并不将类型参数T限制为MyClass的子类。
StevoSlavić,2014年

@StevoSlavić:是吗?这是完全类型安全的。
newacct

如果我正确理解了原始问题/示例,则其中一个想法是将T限制为MyClass的子类,而编译器不允许这样做。
StevoSlavić2014年

@StevoSlavić:问题是希望每个子类仅可与该子类进行比较
newacct 2014年

没有什么阻止您编写公共类SubClass扩展MyClass <WhateverClass> ....
StevoSlavić14年

1

找到了另一个解决方案:

  1. 在组成可映射对象的字段上定义一个接口(例如ComparableFoo)
  2. 在父类上实现接口
  3. 在父类上实现Comparable。
  4. 编写您的实现。

解决方案应如下所示:

public abstract class MyClass implements ComparableFoo,Comparable<ComparableFoo> {
    public int compareTo(ComparableFoo o) {
    // your implementation
    }
}

该解决方案意味着可能有更多东西可以实现ComparableFoo-可能不是这种情况,但是您正在对接口进行编码,并且泛型表达式很简单。


我喜欢这个解决方案,因为它很简单
Pehmolelu,2015年

0

我知道您说过您想要“ compareTo(SubClass object),接受其自身类型的对象”,但我仍然建议声明这样的抽象类:

public abstract class MyClass implements Comparable <Object>

并在覆盖MySubClass中的compareTo时执行instanceof检查:

@Override
public int compareTo(Object o) {
    if (o instanceof MySubClass)) {
        ...
    }
    else throw new IllegalArgumentException(...)
}

类似于“等于”或“克隆”

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.