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).