如何解决Java泛型中交集类型导致的模糊方法?

时间:2009-05-02 22:28:34

标签: java generics

我刚刚发现您可以在单个类型参数绑定中指定多个类型(请参阅示例)。像任何新工具一样,我一直在尝试探索如何使用(和误用)的可能性。我精心设计了这个例子以帮助说明。

在下面的示例中,编译器给出了一个错误

  

dispatch(new AlphabetSoup());

     

方法调度(Demo.Soup)对于Demo类型

是不明确的

我可以理解这一点,因为任一方法签名都匹配。我的问题是如何在不改变方法的情况下解决这个问题?如果我想强制调用Soup版本,我可以向Soup转发:

  

dispatch((Soup)new AlphabetSoup())

但我不确定你是如何强制拨打其他版本的。有可能吗?

public class Demo {

    interface HasA { public char getA(); }
    interface HasB { public char getB(); }
    interface HasC { public char getC(); }

    interface Soup { 
        public void eat();
    }

    class Alphabet implements HasA, HasB, HasC {
        public char getA() { return 'a'; }
        public char getB() { return 'b'; }
        public char getC() { return 'c'; }
    }

    class AlphabetSoup implements Soup,  HasA, HasB, HasC  { 
        public void eat() { System.out.println("Mmm Mmm Good!"); }
        public char getA() { return 'a'; }
        public char getB() { return 'b'; }
        public char getC() { return 'c'; }
    }

    public void dispatch(Soup soup) {
        System.out.println("Eating some soup...");
        soup.eat();
    }

    public <T extends HasA & HasB & HasC> void dispatch(T letters) {
        System.out.println("Reciting ABCs...");
        System.out.println(letters.getA());
        System.out.println(letters.getB());
        System.out.println(letters.getC());
    }

    public void test() {
        dispatch(new Alphabet());
        dispatch(new AlphabetSoup());
    }


    public static void main(String[] args) {
        new Demo().test();
    }
}

- 编辑:刚刚了解到“多个有界类型参数正式称为”交叉类型“

5 个答案:

答案 0 :(得分:11)

请注意,错误与泛型无关,如果使用接口并且类型是交集,则会得到相同的结果:

public class AA {

    interface XX{};
    interface YY{};

    public void doSomething(XX x){}
    public void doSomething(YY x){}

    class XY implements XX,YY{

    }

    public void runner(){
        doSomething(new XY());
    }
}

你在“doSomething”中得到同样的错误,编译器无法解决歧义。你想解释为XX还是YY?你必须用演员表来指定它。 但是如果你有一个层次结构,比如“YY extends XX”和“XY implements YY”,编译器可以推断出正确的调用方法。

答案 1 :(得分:8)

编译器是正确的,可以帮助您避免混乱。

AlphaBetSoup是汤的子类型,也是HasA,HasB和HasC的子类型

因此,它符合两个版本的Dispatch

的账单

由于Soup不是HasA,HasB或HasC的子类型,因此也不能说一个版本比另一个版本更具“特异性”。

因此,您将正确收到错误。

重载方法不应该含糊不清。如果您的类型混合了两种类型并且每种类型都有重载,请更改层次结构或消除过载。使用子类型和重载是错误的。

答案 2 :(得分:2)

让我用一个非常简单的程序解释一下:

下面的代码说明方法对于类型编译器错误是不明确的。

public class AmbiguousMethodOwner {
            void ambiguousMethod(Comparable c){}
            void ambiguousMethod(Serializable c){}
            void test() {
                   ambiguousMethod("bar");
           }
     }

问题现在很明显:因为String实现了Comparable和 Serializable,编译器无法知道您打算调用哪种方法。

简单的演员表可以解决问题:

ambiguousMethod((比较) “栏中的”);

http://www.javaneverdie.com/java/the-method-is-ambiguous-for-the-type/

在我们的案例中,方法调度正在产生问题。见

class AlphabetSoup implements Soup,  HasA, HasB, HasC 

public void dispatch(Soup soup)
 public <T extends HasA & HasB & HasC> void dispatch(T letters) {

现在,如果你调用dispatch(new AlphabetSoup());编译器会混淆应该调用哪个版本的调度?

答案 3 :(得分:1)

请注意,您没有真正的问题,因为您有兴趣调用的方法因动态绑定而被调用。

运行dispatch((Soup) new AlphabetSoup());会产生:

Reciting ABCs...
a
b
c
Eating some soup...
Mmm Mmm Good!

因此,由于基本的多态行为,已调用AlphabetSoup方法。

答案 4 :(得分:1)

不是说你应该保留重载的dispatch方法(因为这个原因我赞成了Uri),但你可以通过尝试强制调用泛型版本:

demo.<AlphabetSoup>dispatch(new AlphabetSoup());

或者用以下方式调用汤版:

demo.dispatch((Soup) new AlphabetSoup());

更好的解决方法是首先不要使用重载的dispatch方法。

void dispatchSoup(Soup soup);
<T extends HasA & HasB & HasC> void dispatchLetters(T letters);