代码之家  ›  专栏  ›  技术社区  ›  Leo Izen

使用超级/子类型重载方法/构造函数

  •  3
  • Leo Izen  · 技术社区  · 15 年前

    对于在某些情况下调用哪个重载方法,我有一些问题。

    案例1:

    public void someMethod(Object obj){
        System.out.println("Object");
    }
    public void someMethod(InputStream is){
        System.out.println("InputStream");
    }
    public void someMethod(FilterInputStream fis){
        System.out.println("FilterInputStream");
    }
    

    我知道如果我通过 String

    案例2:

    这有点棘手,因为它利用了多接口继承。BlockingQueue和Deque都不是彼此的子/超类型,但都是BlockingDeque的超类型。Sun添加了带有接口的多重继承,因为它们不需要树结构。封锁的声明是
    public interface BlockingDeque extends BlockingQueue, Deque { .

    public void someMethod(BlockingQueue bq){
        System.out.println("BlockingQueue");
    }
    public void someMethod(Deque bq){
        System.out.println("Deque");
    }
    public void someCaller(){
         BlockingDeque bd = new LinkedBlockingDeque();
         someMethod(bd);
    }
    

    案例3:

    你可以把这两者结合起来:

    public void someMethod(Queue q){
        //...
    }
    public void someMethod(Deque q){
        //...
    }
    public void someMethod(List p){
        //...
    }
    public void someCaller(){
        someMethod(new LinkedList());
    }
    

    同样的问题:someMethod(Queue)、someMethod(Deque)或someMethod(List)?

    案例4:

    通过引入两个论点,你也可以使事情变得非常复杂:

    public void someMethod(Collection c1, List c2){
        //...
    }
    public void someMethod(List c1, Collection c2){
        //...
    }
    public void someCaller(){
        someMethod(new ArrayList(), new ArrayList());
    }
    

    案例5:

    当它们有不同的返回类型时,情况会变得更糟:

    public Class<?> someMethod(BlockingQueue bq){
        return BlockingQueue.class;
    }
    public String someMethod(Deque bq){
        return "Deque";
    }
    public void someCaller(){
         BlockingDeque bd = new LinkedBlockingDeque();
         System.out.println(someMethod(bd));
    }
    

    这些会变得很糟糕。在这种情况下,打电话的人会打印什么?somethod(BlockingQueue).toString()或somethod(Deque)?

    3 回复  |  直到 15 年前
        1
  •  6
  •   Mark Elliot    15 年前

    一般来说,Java将调用最窄的非二义性定义,因此在前几种情况下,如果传递一个窄类型,它将调用最窄的函数;如果传递一个更宽的类型(比如InputStream),您将获得更宽类型的函数(在情况1中,InputStream是方法2)。 Here's a simple test note that downcasting will widen the type, and call the wider type's method .

    核心问题是Java是否能够解决一个惟一的调用函数。因此,这意味着如果您提供了一个具有多个匹配项的定义,您需要要么匹配已知的最高类型,要么唯一匹配较宽的类型 没有 也匹配更高的类型。基本上:如果您匹配多个函数,其中一个函数需要在层次结构中更高一点,以便Java解决这个差异,否则调用约定是完全不明确的。

    当方法签名不明确时,Java似乎会抛出编译错误。在我看来,案例4是最糟糕的例子,所以我写了一个 quick test

    案例5并没有使事情变得更好或更糟:Java没有使用返回类型来消除要调用哪个方法的歧义,因此它不会帮助您——而且由于定义已经不明确,您最终仍然会出现编译错误。

    所以快速总结:

    1. 由于在使用纯InputStream调用时调用不明确,而使用FilteredInputStream调用时使用第三个def,使用实现InputStream但不是FilteredInputStream的调用时使用第二个def,其他任何东西,第一个def,导致编译错误

    2. 第二定义

    3. 不明确,将导致编译错误

    4. 不明确,将导致编译错误

    5. 不明确,将导致编译错误

    最后,如果您怀疑是否调用了您认为应该调用的定义,您应该考虑更改代码以消除歧义,或者指定正确的类型参数来调用“right”函数。Java将告诉您何时它不能做出明智的决定(当事情确实模棱两可时),但是避免这些问题的最佳方法是通过一致和明确的实现。不要做奇怪的事情,比如案例4,这样你就不会遇到奇怪的问题。

        2
  •  2
  •   Gaurav Saxena    15 年前

    在重载函数的情况下,调用的方法将是 输入对所传递对象的引用。还要注意的是重载方法的绑定是在编译时决定的,而不是在运行时决定的对象类型。

    案例1:如果在编译时输入是InputStream类型,那么将调用第二个方法。BufferedInputStream将进入第二个方法。

    案例2:这在编译时失败,因为BlockingDeque引用类型不明确,并且参数在扩展这两种类型时可以适合这两种方法中的任何一种

    案例3:这里没问题,第三个方法,因为Linkedlist与其他两个参数中的任何一个都不兼容

    案例4:模棱两可,因为有了这些论据,我可以使用这两种方法中的任何一种,而且无法识别

    案例5:返回类型在重载方法中没有角色可扮演。案例2成立。

        3
  •  0
  •   sonnyrao    15 年前

    这与关于重载参数的问题有点相悖,但有一个相当明确的理由说明案例5“更糟”。

    案例5是使用名为co variant return types的语言特性的地方。它最初并不存在于Java中,但我相信它是在v1.5中添加的(部分原因是这个问题)。如果编译器无法找出正确的返回类型,那么它将失败,在本例中就是这样。