2010-08-28 190 views
62

我想要定义一个实现Comparable的抽象类。当我定义,定义如下类:Java抽象类使用泛型实现接口

public abstract class MyClass implements Comparable <MyClass> 

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

public abstract class MyClass implements Comparable <? extends MyClass> 

它抱怨说“超类型不能指定任何通配符”。

有没有解决方案?

回答

40

这在我看来有点太冗长,但工作原理:

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; 
    } 

} 
+0

我认为就是这样。 – Pointy 2010-08-29 00:04:25

+2

考虑(1)class MyImpl1 extends MyClass {...};和(2)MyImpl2类扩展MyClass {public int compareTo(MyImpl1 o){...}}。 MyImpl2没有做正确的事情。 – emory 2010-08-29 00:41:42

+1

如果我们假设每个子类都使用自己的类作为泛型参数来扩展MyClass,那么解决方案是正确的。但是,埃默里指出,似乎没有办法确保这一点。 – Cem 2010-08-29 08:46:09

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 { 
... 
} 

呼叫比较会调用超类方法......

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

c1.compareTo(c2); 
+0

这不正是Cem描述他的问题的方式吗?你将如何使用不同的参数类型在'MyClass1'或'MyClass2'中实现'compareTo'? – whiskeysierra 2010-08-29 00:13:52

+0

你是对的......用另一种方式阅读问题。 – zevra0 2010-08-29 00:16:27

17

除了你遇到声明签名机械的困难,我们的目标没有太大的意义。你正试图建立一个协变比较函数,这个函数打破了建立派生类可以定制的接口的想法。

如果你定义一些子类​​使得它的情况下,只能相对于其他​​实例,则如何​​满足MyClass定义的合同吗?回想一下MyClass是说它和它的任何类型可以与其他MyClass实例进行比较。您正试图使​​不正确,这意味着​​不符合MyClass的合同:由于​​的要求更严格,因此您不能用​​替代MyClass

这个问题主要集中在协方差和反变量上,以及它们如何通过类型派生来改变函数签名。您可以放宽对参数类型的要求 - 接受比超类型的签名要求更宽的类型 - 您可以加强对返回类型的要求 - 承诺返回比超类型签名更窄的类型。这些自由中的每一个仍然允许超类型的派生类型的完美替换;当通过超类型的接口使用派生类型时,调用者无法区分差异,但具体使用派生类型的调用者可以利用这些自由。

Willi's answer教导一些关于泛型声明的内容,但我希望你在接受技术之前重新考虑你的目标,而不要牺牲语义。

+0

我同意这个答案。另外,我们应该编写接口而不是类。实际的实现类可能是匿名类,本地类(嵌套在方法中),私有(嵌套在类中)或者包私有,因此不在我们的范围内。 – emory 2010-08-29 00:46:23

+2

我看到的另一个问题是将子类对象存储在集合中。我应该可以用'List '而不是'List >'来存储它们。当你得到这些对象之一并调用'equals(anObject)'时会调用什么? – TheLQ 2010-08-29 00:59:27

+0

谢谢你的回答。实际上,我正在寻找一个合约,强制每个子类都有一个compareTo()方法,仅用于其自己的类,但不适用于其他任何子类。在这个意义上,我的制造的泛型定义可能会产生误导。 – Cem 2010-08-29 08:31:15

3

看到Java的自己的例子:在SEH的评论

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

:通常的说法是正确的。但泛型使得类型关系更加复杂。子类可以不是MyClass在威利的溶液亚型....

SubClassAMyClass<SubClassA>一个亚型,但不是MyClass<SubClassB>

类型MyClass<X>的子类型定义了一个合同compareTo(X)其中所有其亚型的绝荣誉。那里没有问题。

+1

这是一个很好的例子,但它不同于杰姆的原始问题。现在,我可能从字面上看他的代码;也许这正是他试图写的东西。在这种情况下,Enum接口的Comparable方面是关于一个特定的子类可以对*自身做些什么(或者,而不是它本身的实例),而不是它能用Enum类派生的类型做什么一般。如果这就是杰姆之后的事情,那么我认为你的回答比我的更合适。 – seh 2010-08-29 02:56:00

+0

我想只要所有的子类都是MyClass 的子类型就可以了。但我不确定是否会在未来的步骤中导致问题。这是我第一次用泛型进行设计。 – Cem 2010-08-29 08:34:07

+0

这是一个不好的例子。枚举是一种特殊情况 - 枚举类型由语言提供,并且具有特定的形式(enum A'将实现Enum '),这对于用户定义的类来说并非如此。 – newacct 2012-11-30 10:42:37

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; 
    } 

} 
+0

这不会将类型参数T限制为MyClass的子类。 – 2014-07-21 09:39:54

+0

@StevoSlavić:那么?这是完全类型安全的。 – newacct 2014-07-21 19:03:47

+0

如果我正确理解原始问题/示例,其中一个想法是将T限制为MyClass的子类,而编译器不允许。 – 2014-07-21 21:25:41

0

我知道你说你想“的compareTo(子类对象),接受自己的类型的对象”,但我还是建议声明抽象类是这样的:

public abstract class MyClass implements Comparable <Object> 

,做一个instanceof检查在MySubClass覆盖的compareTo时:

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

类似 '等于' 或 '克隆'

1

发现了另一个解决方案:

  1. 定义在其上构成comaprable(例如ComparableFoo)的字段的接口
  2. 实现父类的接口
  3. 实现对父类具有可比性。
  4. 编写您的实施。

解决方案应该是这样的:

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

这种解决方案意味着更多的东西有可能实现的ComparableFoo - 这很可能并非如此,但那么你的编码到一个接口和仿制药的表达很简单。

+0

我喜欢这个解决方案,因为它很简单 – Pehmolelu 2015-08-24 06:14:46