代码之家  ›  专栏  ›  技术社区  ›  itsadok

如何解决Java泛型中交集类型导致的不明确方法?

  •  8
  • itsadok  · 技术社区  · 17 年前

    我最近发现,可以在一个类型参数绑定中指定多个类型(参见示例)。和任何新工具一样,我一直在尝试探索如何使用(和误用)这个工具。我制作了这个例子来帮助说明。

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

    分派(新字母组());

    对于Demo类型,方法dispatch(Demo.Soup)是不明确的

    我可以理解这一点,因为两种方法的签名都匹配。我的问题是,如何在不改变方法的情况下解决这个问题?如果我想强制调用“汤”版本,我可以将其降级为“汤”:

    分派((Soup)新字母组())

    但我不确定你会怎么强迫打电话给另一个版本。可能吗?

    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 回复  |  直到 17 年前
        1
  •  11
  •   GClaramunt    17 年前

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

    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扩展XX”和“XY实现YY”,编译器可以推断出正确的调用方法。

        2
  •  8
  •   Uri    17 年前

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

    AlphaBetSoup是汤的一个亚型,也是HasA、HasB和HasC的一个亚型

    因此,它适合两种版本的分派

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

    因此,您将正确地获得错误。

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

        3
  •  4
  •   AZ_    14 年前

    让我用一个非常简单的程序来解释这一点:

    下面的代码说明了由于类型编译器错误,该方法的原因是不明确的。

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

    现在的问题很明显:因为String实现了Comparable和Comparable 可序列化,编译器无法知道要调用哪个方法。

    一个简单的演员阵容就能解决这个问题:

    模棱两可的方法((可比较的)“酒吧”);

    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()); 编译器会对应该调用哪个版本的分派感到困惑?

        4
  •  1
  •   Yuval Adam    17 年前

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

    跑步 dispatch((Soup) new AlphabetSoup()); 产量:

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

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

        5
  •  1
  •   Steve Reed    17 年前

    并不是说你应该保持超负荷 dispatch 方法(出于这个原因,我对Uri进行了投票),但您可以通过尝试以下操作来强制调用泛型版本:

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

    或者将汤版本称为:

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

    不过,解决这个问题的更好办法是不要让电脑超载 派遣 首先是方法。

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