Let's skip the fact that you ignored an unchecked cast warning for now and try to understand why this happened.
In this statement:
Test.m3(Test.m4("1"));
There is one inferred type, which is the return type of m4
. If one is to use it outside the m3
invocation context, as in:
Test.m4("1"); // T is Object
T
is inferred as Object
. One can use a type witness to force the compiler to use a given type:
Test.<String>m4("1"); // T is String
...or by using the expression in an assignment context:
String resString = Test.m4("1"); // T is String
Integer resInt = Test.m4("1"); // T is Integer <-- see the problem?
... or in an invocation context:
Integer.parseInt(Test.m4("1")); // T is String
Long.toString(Test.m4("1")); // T is Long
Now, back to Test.m3(Test.m4("1"));
: I couldn't find a reference for this, but I believe the compiler is forced to make T
resolve to the parameter type of m3
, which is Object[]
. This means that T
, which has to coincide with the parameter type of m3
, is therefore resolved to Object[]
, and that makes it as though you specified generic types as:
Test.m3(Test.<Object[]>m4("1")); // this is what is happening
Now, because m4
is not returning an Object[]
, m3
is receiving a String
, which leads to the inescapable ClassCastException
.
How to solve it?
The first way to fix this is to specify a correct type argument for m4
:
Test.m3(Test.<String>m4("1"));
With this, String
is the return type of m4
, and m3
is called with a single String
object (for the Object...
var-arg), as if you had written:
String temp = m4("1");
m3(temp);
The second approach was suggested in @Ravindra Ranwala's deleted answer. In my opinion, this boils down to heeding compiler warnings:
public static <T> T m4(Object s) {
return (T) s; // unchecked cast
}
The unchecked cast warning simply tells you that the compiler (and the runtime) are not going to enforce type compatibility, simply because T
is not known where you cast. The following version is type-safe, but it also makes the compiler use String
as the return type of m4
as well as the type of the parameter to m3
:
public static <T> T m4(T s) {
return s;
}
With this, m3(m4("1"));
still uses Object...
as the parameter type of m3
, while keeping String
the return type of m4
(i.e., a string value is used as the first element of the Object
array).