Issue
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?
Solution
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 totoString(Object)
- But there are things you can pass to
toString(Object)
that can't be passed totoString(Collection<Object>)
(such asnew Object()
).
This makes the toString(Collection<Object>)
more specific, so this is the one that is chosen.
Answered By - Andy Turner
Answer Checked By - Cary Denson (JavaFixing Admin)