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

时间:2010-10-17 02:58:49

标签: java inheritance overloading

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

案例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,它将打印“对象”。但是,如果我传递一个InputStream怎么办?如果我传递一些像BufferedInputStream这样的东西会让人感到困惑。这会调用Object one,InputStream one还是FilterInputStream?方法的顺序是否重要?

案例2:

这有点棘手,因为它利用了多个接口继承。 BlockingQueue和Deque都不是彼此的子/超类型,但两者都是BlockingDeque的超类型。 Sun通过接口添加了多重继承,因为它们不需要树结构。 BlockingDeque的声明是
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);
}

这会调用someMethod(BlockingQueue)还是someMethod(Deque)?

案例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());
}

这会调用someMethod(Collection,List)还是反之?

案例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));
}

这些可能会非常糟糕。在这种情况下,someCaller会打印什么? someMethod(BlockingQueue).toString()或someMethod(Deque)?

3 个答案:

答案 0 :(得分:6)

一般来说,Java会调用最窄的非模糊定义,所以对于前几种情况,如果你传递一个窄类型,它会调用最窄的函数,如果你传递一个更宽的类型(比如InputStream),你会得到更广泛的类型函数(对于InputStream的情况1,方法2)。 Here's a simple testnote that downcasting will widen the type, and call the wider type's method

核心问题是Java是否可以解析一个独特的调用函数。这意味着如果您提供具有多个匹配项的定义,则需要匹配最高已知类型,或者唯一匹配更宽类型也匹配更高类型。基本上:如果你匹配多个函数,其中一个函数需要在层次结构中更高,以便Java解决差异,否则调用约定肯定是模棱两可的。

当方法签名不明确时,Java似乎会抛出编译错误。在我看来,案例4在规范上是最糟糕的例子,所以我写了一个quick test并且确实得到了预期的编译错误,抱怨函数调用的模糊匹配。

案例5没有做出更好或更坏的事情:Java不使用返回类型来消除要调用哪种方法的歧义,因此它对您没有帮助 - 而且由于定义已经模棱两可,您仍然会最终出现编译错误。

所以快速摘要:

  1. 使用普通InputStream调用时由于调用模糊而导致的编译错误,使用FilteredInputStream调用使用3rd def,调用实现InputStream但不是FilteredInputStream的东西使用2nd def,其他任何东西,1st def

  2. 2nd def

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

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

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

  6. 最后,如果您怀疑自己正在调用您认为应该是的定义,那么您应该考虑更改代码以消除歧义,或者指定正确的类型参数来调用“正确”函数。 Java会告诉你什么时候它不能做出明智的决定(当事情真的很模糊时),但避免任何这些问题的最好方法是通过一致和明确的实现。不要做奇怪的事情,比如案例4,你不会遇到奇怪的问题。

答案 1 :(得分:2)

在重载函数的情况下,被调用的方法将是对被传递的对象的引用具有最多受限但兼容的参数类型的方法。还需要注意的是,重载方法的绑定是在编译时确定的,而不是在runtime.e.g中确定的对象类型。

情况1:如果在编译时输入类型为InputStream,则将调用第二个方法。 BufferedInputStream将进入第二种方法。

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

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

案例4:模棱两可,因为有了这些论点,我可以进入这两种方法中的任何一种,而且无法辨别

案例5:返回类型在重载方法中没有任何作用。案例2成立。

答案 2 :(得分:0)

这与关于重载参数的问题有点相关,但是有一个非常清晰的理由说明案例5“更糟”。

案例5是您使用称为共变量返回类型的语言功能的地方。这不是最初存在于Java中,而是在v1.5中添加,我相信(部分是因为这个问题)。如果编译器无法弄清楚正确的返回类型是什么,那么它就失败了,这就是在这种情况下会发生的事情。