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

Leo*_*zen 3 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");
}
Run Code Online (Sandbox Code Playgroud)

我知道,如果我通过它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);
}
Run Code Online (Sandbox Code Playgroud)

这会调用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());
}
Run Code Online (Sandbox Code Playgroud)

同样的问题: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());
}
Run Code Online (Sandbox Code Playgroud)

这会调用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));
}
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*iot 6

一般来说,Java会调用最窄的非模糊定义,因此对于前几种情况,如果传递一个窄类型,它将调用最窄的函数,如果你传递一个更宽的类型(比如InputStream),你会获得更宽泛的类型的函数(在用于InputStream的case 1,即方法2).这是一个简单的测试,并注意向下转换会扩大类型,并调用更宽泛类型的方法.

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

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

案例5没有做任何更好或更糟的事情:Java不使用返回类型来消除调用哪个方法的歧义,因此它不会帮助你 - 而且由于定义已经含糊不清,你仍然会最终得到编译错误.

所以快速总结:

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

  2. 第二次定义

  3. 模棱两可,会导致编译错误

  4. 模棱两可,会导致编译错误

  5. 模棱两可,会导致编译错误

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