假设我们有一个带有compare()函数的Parent接口。
public interface Parent {
public int compare(Parent otherParent);
}
假设子项Child1,Child2,Child3实现此接口Parent
public class Child1 implements Parent {
@Override
public int compare(Parent other) {
Child1 otherChild = (Child1)other;
}
}
此外,我在代码中的其他地方使用泛型<T extends Parent>
。所以我需要从代码的其他部分比较两个类型为T的对象。
我理解这是一个糟糕的设计,因为我在compare()函数中对Parent对象进行类型转换,我甚至不知道输入是否为Child1类型。
如何在使用泛型时避免使用此类型转换?
答案 0 :(得分:5)
为什么不呢?
interface Parent<T extends Parent<?>> {
int compare(T type);
}
class Child1 implements Parent<Child1>{
@Override
public int compare(Child1 type) {
return 0;
}
}
修改:为了确保正确使用,您可以使用
interface Parent<T extends Parent<T>>{ /* ... */ } //instead of wildcard
但说实话,“循环”看起来并不漂亮,而且由于Java中的Generics在RunTime(more information)不起作用,因此它们基本上是语法糖,因为你称之为“糟糕的设计“所以我认为你目前的方法不好。
答案 1 :(得分:3)
答案取决于您如何对待继承层次结构:
instanceof
以下是使用抽象类的示例实现:
// Use this class as the base class of your Child classes
class AbstractChild implements Parent {
@Override
public int compare(Parent otherObj) {
if (!()) {
throw new IllegalStateException("Unexpected implementation of Child");
}
AbstractChild other = (AbstractChild)otherObj;
return doCompare(other);
}
protected abstract int doCompare(AbstractChild other);
protected abstract int accept(Child1 c1);
protected abstract int accept(Child2 c2);
protected abstract int accept(Child3 c3);
}
class Child1 extends AbstractChild {
@Override
protected int doCompare(AbstractChild other) {
return other.accept(this);
}
@Override
protected int accept(Child1 c1) {
// Compare c1 instance to this object
}
@Override
protected int accept(Child2 c2) {
// Compare c2 instance to this object
}
@Override
protected int accept(Child3 c3) {
// Compare c3 instance to this object
}
}
class Child2 extends AbstractChild {
@Override
protected int doCompare(AbstractChild other) {
return other.accept(this);
}
@Override
protected int accept(Child1 c1) {
// Compare c1 instance to this object
}
@Override
protected int accept(Child2 c2) {
// Compare c2 instance to this object
}
@Override
protected int accept(Child3 c3) {
// Compare c3 instance to this object
}
}
class Child3 extends AbstractChild {
@Override
protected int doCompare(AbstractChild other) {
return other.accept(this);
}
@Override
protected int accept(Child1 c1) {
// Compare c1 instance to this object
}
@Override
protected int accept(Child2 c2) {
// Compare c2 instance to this object
}
@Override
protected int accept(Child3 c3) {
// Compare c3 instance to this object
}
}
此方法中唯一的演员是抽象类级别。实际比较逻辑的实现包含在accept
方法中,其中比较是在两个已知类型的对象之间完成的。
答案 2 :(得分:2)
你不能。 Java接口正如其名称所暗示的那样 - 一个界面决定了接受的类型;所以在接口中定义的编译时需要完全相同的方法签名。
是的,将其转换为Child类型感觉就像糟糕的设计 - 但它确实是Java规定的糟糕设计,所以这不是你的错。
答案 3 :(得分:2)
在一般情况下,你无法避免它。但是,如果子类的数量是固定的,您可以应用类似于访问者模式的东西,以避免在编写更多代码时付出代价:
interface Parent {
int compare(Parent p);
protected int compareWith(Child1 c);
protected int compareWith(Child2 c);
protected int compareWith(Child3 c);
}
class Child1 implements Parent {
@Override int compare(Parent p) {
return p.compareWith(this);
}
@Override int compareWith(Child1 c) {
//specific code to child1
}
@Override int compareWith(Child2 c) {
//specific code to child2
}
@Override int compareWith(Child3 c) {
//specific code to child3
}
}
// and so on for the other children
这确实可以避免施放,但我不确定在你在这里展示的情况下额外的努力是值得的。
答案 4 :(得分:2)
您描述的模式完全模式为Comparable
。实际上,您应该考虑省略Parent
界面,并将其替换为Comparable
。
Comparable
接口及其用法也显示了如何解决这个问题:类型可以作为参数给出,以确保只有匹配类型可以传递给compare
方法:
interface Parent<T> {
int compare(T that);
}
class Child1 implements Parent<Child1> {
@Override
public int compare(Child1 that) {
...
}
}
对于所有其他情况,您至少必须考虑当不同的Child
- 类相互比较时会发生什么:
Child1 c1 = new Child1();
Child2 c2 = new Child2();
// Should this be possible? What should happen here?
// How should different child classes be compared?
c1.compare(c2);