Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java static polymorphism (overloading) and inheritance between generics

Java 11 (may be irrelevant):

public static String toString(Object obj) {
    return ReflectionToStringBuilder.toString(obj, ToStringStyle.SHORT_PREFIX_STYLE);
}

public static String toString(Collection<Object> collection) {
    return collection.stream()
            .map(SaLogUtils::toString)
            .collect(Collectors.joining(", ", "[", "]"));
}

public static void main(String[] args) {
    List<Integer> list = List.of(Integer.valueOf(1));
    System.out.println(SaLogUtils.toString(list));
    System.out.println(SaLogUtils.toString(List.of(Integer.valueOf(1))));
}

Surprising output:

// from toString(Object)
ImmutableCollections.List12[e0=1,e1=<null>]
// from toString(Collection<Object>)
[Integer[value=1]]

Why does Java statically choose different methods?

like image 828
gavenkoa Avatar asked Feb 04 '20 14:02

gavenkoa


People also ask

Can I use polymorphism in generics?

The polymorphism applies only to the 'base' type (type of the collection class) and NOT to the generics type.

What is the connection between generics and polymorphism?

Using Java Generic concept, we might write a generic method for sorting an array of objects, then invoke the generic method with Integer arrays, Double arrays, String arrays and so on, to sort the array elements. Polymorphism is the ability of an object to take on many forms.

Is overriding and overloading both static polymorphism?

Method overriding is an example of dynamic polymorphism, while method overloading is an example of static polymorphism.

Is polymorphism and overloading same?

Polymorphism means more than one form, same object performing different operations according to the requirement. Method overloading means writing two or more methods in the same class by using same method name, but the passing parameters is different.


1 Answers

When there are multiple overloads which could be invoked, Java chooses the most specific applicable method:

The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error. In cases such as an explicitly typed lambda expression argument (§15.27.1) or a variable arity invocation (§15.12.2.4), some flexibility is allowed to adapt one signature to the other.


toString(Collection<Object>) isn't applicable for a List<Integer>, because a List<Integer> isn't a List<Object>, so it's not a Collection<Object> either. As such, only the toString(Object) method is applicable, so that's the one that is invoked.


toString(Collection<Object>) is applicable for List.of(someInteger) because that List.of is a polyexpression: it could be List<Integer>, it could be List<Object>, it could be List<Serializable>.

Since both toString(Object) and toString(Collection<Object>) are applicable, it has to choose one or the other (or declare it ambiguous). The Collection overload is more specific because:

  • Anything you pass to toString(Collection<Object>) can also be passed to toString(Object)
  • But there are things you can pass to toString(Object) that can't be passed to toString(Collection<Object>) (such as new Object()).

This makes the toString(Collection<Object>) more specific, so this is the one that is chosen.

like image 105
Andy Turner Avatar answered Nov 15 '22 03:11

Andy Turner