Call a method whose parameter is limited by the type of intersection

In the context of static methods, I would like to narrow the type reference and call a more specific method for such an object:

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 would like to know if there is a syntax to call do (Iterable), namely, using some kind of hack like this:

 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 that it is not possible to execute my iteration in this way.

 do((List<? extends T> & RandomAccess) iterable); 

and it seems to me that erasing L in

 L extends List<? extends T> & Comparable 

is an

 List<? extends T> 


So why can't I call the method this way?

 do((List<? extends T>) iterable); // Which results in invoking do(Iterable<? extends T) 
+6
source share
3 answers

Your assumption is correct: erasing intersections is the first type - i.e. List (for reflection purposes, etc.).

You can get your code to compile without “hacking” by providing the type of intersection that should be selected as the type of method:

 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 is compiled, and the second method calls, if desired, the first method.

There is no way to throw at the intersection without this technique.


Please note that your code does not compile because do is the java keyword and therefore is not a valid method name. Instead, I used method() to get a compiled example.

Also I think you meant RandomAccess , where you encoded Comparable .

+2
source

AFAIK Erase L extends List<? extends T> & RandomAccess L extends List<? extends T> & RandomAccess will be a List , but even if I am mistaken, you could not use (List<? extends T>) iterable , since it would not meet the requirements (a List not necessarily RandomAccess ). Thus, the only way that would be consistent would be the Iterable version.

Edit: A quick reflection printout confirms my assumption. I called the methods method (creative, right?) And get the following output:

 ... method(java.util.List) ... method(java.lang.Iterable) 

Now you might think that casting on a List would be sufficient (and maybe if you turn off generics or call a method through reflection), but the compiler does not suffer from style erasure and therefore knows that only method(java.lang.Iterable) matches .

+1
source

I don't think there is a way to invoke an erasable version of a static method (in a clean assembly)

 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 call the erasable version of instance methods via 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 } 

Return 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 generated as

 #1 static void foo(List<?> list){} 

but # 2 is kept as it is, with a raw type. How is it that number 2 is still compiling? The spectrum (15.12.2.2) allows you to make uncontrolled conversion possible. An untested conversion can convert raw Foo to Foo<..> (but no more complicated than Foo -> Foo<..>&Bar ).

The spectrum contains only enough tricks to ensure that legacy code can survive typical generation. Your case, of course, is not typical, and tricks do not apply.

+1
source

Source: https://habr.com/ru/post/950086/


All Articles