Java多态:如何避免类型转换输入参数?

时间:2016-07-24 11:57:51

标签: java generics casting polymorphism

假设我们有一个带有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类型。

如何在使用泛型时避免使用此类型转换?

5 个答案:

答案 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);