使用超级/子类型重载方法/构造函数
我对在某些情况下会调用哪个重载方法有一些疑问。
情况 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
它会打印“Object”。但是,如果我向它传递一个输入流怎么办?如果我向它传递诸如 BufferedInputStream 之类的东西,它会变得更加混乱。这会调用 Object 1、InputStream 1、还是 FilterInputStream 1?方法出现的顺序重要吗?
情况 2:
这有点棘手,因为它利用了多个接口继承。 BlockingQueue 和 Deque 都不是彼此的子/超类型,但都是 BlockingDeque 的超类型。 Sun 通过接口添加了多重继承,因为它们不需要树结构。 BlockingDeque 的声明是公共接口BlockingDeque扩展了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)?
I have some questions as to which overloaded method would be called in certain cases.
Case 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");
}
I know that if I pass it a String
it will print "Object". However, what if I pass it an InputStream? It gets more confusing if I pass it something such as BufferedInputStream. Will this call the Object one, the InputStream one, or the FilterInputStream one? Does the order that the methods appear matter?
Case 2:
This is a little more tricky, because it takes advantage of multiple interface inheritance. Neither BlockingQueue and Deque are sub/supertypes of each other, but both are supertypes of BlockingDeque. Sun added multiple inheritance with interfaces because they don't need a tree structure. The declaration for BlockingDeque ispublic 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);
}
Will this Call someMethod(BlockingQueue) or someMethod(Deque)?
Case 3:
You can combine these two with this:
public void someMethod(Queue q){
//...
}
public void someMethod(Deque q){
//...
}
public void someMethod(List p){
//...
}
public void someCaller(){
someMethod(new LinkedList());
}
Same question: someMethod(Queue), someMethod(Deque), or someMethod(List)?
Case 4:
You can make things very complicated too, by introducting two arguments:
public void someMethod(Collection c1, List c2){
//...
}
public void someMethod(List c1, Collection c2){
//...
}
public void someCaller(){
someMethod(new ArrayList(), new ArrayList());
}
Will this call someMethod(Collection, List) or vice versa?
Case 5:
It gets worse when they have different return types:
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));
}
These can get pretty bad. What will someCaller print in this case? someMethod(BlockingQueue).toString(), or someMethod(Deque)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
一般来说,Java 将调用最窄的非二义定义,因此对于前几种情况,如果您传递窄类型,它将调用最窄的函数,如果您传递更宽的类型(例如 InputStream),您将获得更宽类型的函数(在InputStream 的情况 1 即为方法 2)。 这是一个简单的测试,以及请注意,向下转型会加宽类型,并调用更宽类型的方法。
核心问题是Java能否解析出唯一的函数来调用。因此,这意味着,如果您提供具有多个匹配项的定义,则需要匹配最高的已知类型,或者唯一匹配更宽的类型,而无需匹配更高的类型。基本上:如果您匹配多个函数,其中一个函数的层次结构需要更高,以便 Java 解决差异,否则调用约定绝对是不明确的。
当方法签名不明确时,Java 似乎会抛出编译错误。在我看来,案例 4 是典型的最糟糕的例子,所以我写了一个快速测试,并且实际上得到了预期的编译错误,抱怨要调用的函数匹配不明确。
情况 5 并没有使任何事情变得更好或更糟:Java 不使用返回类型来消除调用哪个方法的歧义,因此它不会帮助您 - 并且由于定义已经不明确,您仍然会得到编译错误。
因此,快速总结:
使用普通 InputStream 调用时由于不明确的调用而导致编译错误,使用 FilteredInputStream 调用时使用第三个 def,使用实现 InputStream 但不是 FilteredInputStream 的内容调用时使用第二个 def,其他任何东西,第一个 def< /p>
第二个 def< /p>
不明确,将导致编译错误
不明确,将导致编译错误
不明确,会导致编译错误
最后,如果您怀疑自己正在调用您认为应该调用的定义,则应该考虑更改代码以消除不明确性或努力指定正确的类型参数来调用“正确的”函数。 Java 会告诉您何时无法做出明智的决定(当事情确实不明确时),但避免任何这些问题的最佳方法是通过一致且明确的实现。不要做奇怪的事情,比如案例 4,这样你就不会遇到奇怪的问题。
In general, Java will invoke the narrowest non-ambiguous definition, so for the first few cases if you pass a narrow type it will invoke the narrowest function, if you pass a wider type (say InputStream) you get the wider type's function (in case 1 for InputStream that's method 2). Here's a simple test, and note that downcasting will widen the type, and call the wider type's method.
The core issue is whether Java can resolve a unique function for calling. So that means if you provide a definition that has multiple matches, you need to either match the highest known type, or uniquely match a wider type without also matching the higher type. Basically: if you match multiple functions, one of them needs to be higher in hierarchy for Java to resolve the difference, otherwise the calling convention is definitively ambiguous.
Java seems to throw a compilation error when the method signatures are ambiguous. In my view Case 4 is canonically the worst example of this, so I wrote a quick test and did in fact get the expected compilation error, complaining of an ambiguous match for functions to invoke.
Case 5 doesn't make anything better or worse: Java doesn't use return type to disambiguate which method to call, so it won't help you -- and since the definitions are already ambiguous you're still going to end up with a compilation error.
So the quick summary:
Compilation error due to ambiguous call when invoked with a plain InputStream, called with FilteredInputStream uses 3rd def, called with something that implements InputStream but isn't a FilteredInputStream uses 2nd def, anything else, 1st def
2nd def
ambiguous, will cause a compilation error
ambiguous, will cause a compilation error
ambiguous, will cause a compilation error
Finally, if you have doubts that you're calling the definition you think you should be, you should consider changing your code to remove the ambiguity or work to specify the right type argument(s) to call the "right" function. Java will tell you when it can't make a smart decision (when things are truly ambiguous), but the best way to avoid any of these problems is through consistent and unambiguous implementations. Don't do weird stuff, like case 4, and you won't run into weird problems.
在重载函数的情况下,调用的方法将是引用所传递的对象时具有最受限制但兼容的参数类型的方法。另外需要注意的是,重载方法的绑定是在编译时决定的,而不是由运行时确定的对象类型决定的。
例如情况1:如果编译时输入是InputStream类型,则将调用第二个方法。 BufferedInputStream 将进入第二种方法。
情况 2:这在编译时失败,因为 BlockingDeque 引用类型不明确,并且参数可以适合这两种方法中的任何一种,因为它扩展了这两种类型
情况 3:这里没问题,第三种方法,因为 Linkedlist 与任何其他两个参数
案例 4:不明确,因为有了这些参数,我可以进入这两个方法中的任何一个,但无法辨别
案例 5:返回类型在重载方法中不起任何作用。情况 2 成立。
In the case of overloaded functions, the method called will be the one which has the most restricted but compatible argument type in reference to the object being passed. Also something to note is that binding of overloaded method is decided at the compile time and not by the type of object determined at the runtime.e.g.
Case 1: If input is of type InputStream at compile time, then 2nd method will be called. BufferedInputStream will go into 2nd method.
Case 2: This fails at compile time because BlockingDeque type of reference is ambiguous and the argument could fit in any of the two methods as it extends both these types
case 3: No problem here, 3rd method because Linkedlist is not compatible with any of the two other arguments
Case 4: Ambiguous because with these arguments I can get into any of those two methods and there is no way to discern
Case 5: Return types have no role to play in overloaded methods. Case 2 holds.
这与关于重载参数的问题有点相切,但是有一个非常明确的原因可以解释为什么情况 5“更糟糕”。
情况 5 是您使用称为协变返回类型的语言功能的情况。这最初并不存在于 Java 中,但我相信是在 v1.5 中添加的(部分是因为这个问题)。如果编译器无法确定正确的返回类型是什么,那么它就会失败,这就是本例中发生的情况。
This is a bit tangential to the question about the overloaded arguments, but there is a pretty crisp reason why case 5 is "worse".
Case 5 is where you're using the language feature called co-variant return types. This wasn't originally present in Java but was added in v1.5 I believe (partially because of this problem). If the compiler cannot figure out what the proper return type is it was fail and that is what happens in this case.