In the context of static methods, i'd like to narrow a type reference and invoke a more specific method for an object like this :
public static <T, L extends List<? extends T> & RandomAccess> void do(L list) {
// Do some stuff
}
public static <T> void do(Iterable<? extends T> iterable) {
if(iterable instanceof List && iterable instanceof RandomAccess)
// invoke do(List&RandomAccess) method
else
// do something else
}
So i'd like to know if there's a syntax allowing to call do(Iterable) rather that using some hack like this one :
private static <L extends List<? extends T> & Comparable> L cast(Iterable<? extends T> iterable) {
return (L) iterable;
}
public static <T> void do(Iterable<? extends T> iterable) {
if(iterable instanceof List && iterable instanceof RandomAccess)
do(cast(iterable));
else
// do something else
NOTE :
I know it isn't possible to cast my iterable this way
do((List<? extends T> & RandomAccess) iterable);
and it seams to me that the erasure of L in
L extends List<? extends T> & Comparable
is
List<? extends T>
So why can't i invoke the method this way ?
do((List<? extends T>) iterable); // Which results in invoking do(Iterable<? extends T)
Your assumption is correct: The erasure of intersections is the first type - ie List
(for purposes of reflection etc).
You can get your code to compile without the "hack" by providing the intersection type to which to cast as a method type:
public static <T, L extends List<? extends T> & RandomAccess> void method(L list) {
// do whatever
}
@SuppressWarnings("unchecked") // needed to suppress unsafe cast warning
public static <T, L extends List<? extends T> & RandomAccess> void method(Iterable<? extends T> iterable) {
if(iterable instanceof List && iterable instanceof RandomAccess)
method((L)iterable); // calls the other method
else
return; // do whatever
}
This code compiles and the second method calls the first method, as desired.
There is no way to cast to an intersection without this technique.
Note that your code does not compile because do
is a java keyword and thus is not a valid method name. I used method()
instead to get a compilable example.
Also I think you meant RandomAccess
where you coded Comparable
.
AFAIK the erasure of L extends List<? extends T> & RandomAccess
would be List
, but even if I'm wrong, you couldn't use (List<? extends T>) iterable
since that would not meet the requirements (a List
is not necessarily RandomAccess
). Thus, the only method that would match would be the Iterable
version.
Edit: a quick reflection printout confirms my assumption. I named the methods method
(creative, isn't it?) and get the following output:
... method(java.util.List)
... method(java.lang.Iterable)
You might now think that casting to List
would be sufficient (and it might be, if you'd disable generics or call the method via reflection), but the compiler doesn't suffer from type erasure and thus knows that only method(java.lang.Iterable)
matches.
I don't think there's a way to invoke erased version of a static method (in a clean build)
public static <L extends List<?> & RandomAccess> void do1(L list) {}
private static <L extends List<?> & RandomAccess> L cast(Iterable<?> iterable)
{
return (L) iterable;
}
public static void do2(Iterable<?> iterable)
{
do1(cast(iterable));
// try to invoke the erased version
//do1((List)iterable); // does not compile
}
We can invoke erased version of instance methods through raw type
static class Helper<T>
{
<T, L extends List<? extends T> & RandomAccess> void do3(L list)
{
do1(list);
}
}
public static void do2(Iterable<?> iterable)
{
Helper helper = new Helper(); // raw type
helper.do3((List) iterable); // erased method
}
Back to static methods. Suppose we have this pre-Java5 code
#1 static void foo(List list){}
#2 foo(new ArrayList());
now after Java5, #1 is generified as
#1 static void foo(List<?> list){}
but #2 is kept as is, with raw type. How come #2 still compiles? The spec (15.12.2.2) makes it possible by allowing an unchecked conversion. The unchecked conversion can convert a raw Foo
to Foo<..>
(but no more sophistication like Foo -> Foo<..>&Bar
).
The spec only contains enough tricks to make sure legacy code can survive typical generification. Your case is certainly not typical, and the tricks don't apply.
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