class SimpleTest {
void met( Object a ) {
println "Object"
}
void met( String b ) {
println "String"
}
static main( args ) {
SimpleTest i = new SimpleTest()
i.met(null)
}
}
This code will produce the output "Object". It will not choose the most specialized version of the method. In this case String is more specialized than Object, so this rule does not apply.
You cannot pass the null value as a parameter to a Java scalar type method; Java scalar types are always non-nullable. However, Java object types can accept null values.
When we pass a null value to the method1 the compiler gets confused which method it has to select, as both are accepting the null. This compile time error wouldn't happen unless we intentionally pass null value.
Method overloading is a form of polymorphism in OOP. Polymorphism allows objects or methods to act in different ways, according to the means in which they are used. One such manner in which the methods behave according to their argument types and number of arguments is method overloading.
The most important rule of method overloading is that two overloaded methods must have different parameters.
Groovy uses a distance calculation approach. Basically if you imagine the classes and interfaces as nodes in a graph and them being connected by their inheritance relationship, then we kind of look for the distance from our given argument type (the runtime type of the argument) to our parameter type (the static type the method parameter has). The connections have different weights, basically going to the super class means a distance of I think 3, to an interface 1, wrapping a primitive is also 1, vargs wrapping has also a weight (and cannot really be represented in the graph anymore, so sorry for the slightly failing image)
In case of null this cannot work of course. Here we look at the distance of the parameter type to Object instead. While the none-null case is the most possible special method we take for the null part the most general one instead. In Java you would normally have the static type or use a cast to ensure what is to be selected. In Groovy we don't have a static type and what is most special can often not be decided correctly. Thus we decided for the most general approach instead for that case. It works really well in general.
Object then is kind of like a fallback, that allows you central null handling. In future versions we may allow the usage of an explicit null type, which then would be preferred over Object if there.
While you can often see directly the distance approach for classes, it is a bit more complicated for interfaces. Basically the algorithm goes like this: If my current class directly implements the interface we are looking for, then it is a match with distance 1. If any of the interfaces the class implements has the interface we look for as parent, then count the "hops" till we are there as distance. But we look for the shortest distance. So we also look the same way at the super class. Any search result from there will have that distance +1 (for the super class hop). If the super class search gives a shorter distance than the search on the implementing interfaces, the super class search result will be taken instead.
As for handling null with interfaces... The distance to Object is here 1 if the interface does not extend another. If it does it is the distance of the parent interface +1. If multiple interfaces are extended, it is the shortest path again.
Let us look at List and Integer for null.
List extends Collection, Collection extend Iterable, Iterable has no parent. That makes a distance of 1 for Iterable, 2 for Collection and finally 3 for List.
Integer extends Number, Number extends Object. Since we hop two times we have a distance of 6 here (2x3), being much bigger than the other case. Yes, that means in general we prefer interfaces. We do that for practical reasons actually, since this way proofed to be most near actual programming practice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With