2013-07-23 2 views
6

В контексте статических методов, я хотел бы узкого ссылочного типа и вызывать более конкретный метод для объекта, например так:вызвать метод параметр, ограничен типом пересечения

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 
} 


Так что я хотел бы знать, если есть синтаксис позволяет называть делать (Iterable) скорее, с помощью какого-то хака, как это:

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 


Примечания: Я знаю, что это не возможно, чтобы бросить мою итерацию этого пути

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

и швы мне, что стирание L в

L extends List<? extends T> & Comparable 

является

List<? extends T> 


Так почему я не могу вызвать метод этот путь?

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

ключевое слово 'do' для петель –

+0

Конечно ... это просто пример ... –

+0

Похоже,' и RandomAccess> 'может быть упрощено до' & RandomAccess> 'или' & RandomAccess> ', по крайней мере для этих подписей. –

ответ

2

Ваше предположение верно: стирание пересечений - это первый тип - то есть List (для целей отражения и т. Д.).

Вы можете получить код для компиляции без «взлома», предоставляя типа пересечения, к которому следует отбрасывать как тип метода:

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 
} 

Этот код компилируется и второй метод вызывает первый метод, так как желательно.

Невозможно бросить на пересечение без этой техники.


Обратите внимание, что ваш код не компилируется, потому что do является Java ключевое слово и, таким образом, не является допустимым именем метода. Вместо этого я использовал method(), чтобы получить компилируемый пример.

Также, я думаю, вы имели в виду RandomAccess, где вы закодировали Comparable.

+0

Мне нравятся простые решения, такие как в этом случае добавление 'l' to' method (Iterable ..) 'generic types. +1 – Pshemo

+0

Я в первую очередь подумал о вашем решении и отбросил его, потому что мне не нравился этот дополнительный параметр типа: это звучало как перерыв в инкапсуляции для меня. Хотя, прочитав свой пост, я должен признать, что это решение короткое и прагматичное =) –

+0

Это сделано =) Как я попросил _syntax_, я думаю, что это отвечает на лучшее =) –

1

AFAIK стиранием L extends List<? extends T> & RandomAccess будет List, но даже если я ошибаюсь, вы не можете использовать (List<? extends T>) iterable так, что не будет отвечать требованиям (а List не обязательно RandomAccess). Таким образом, единственным способом, который будет соответствовать, будет версия Iterable.

Редактировать: распечатка быстрого отражения подтверждает мое предположение. Я назвал методы method (творческий, не так ли?) И получить следующий вывод:

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

Теперь вы можете подумать, что приведение к List было бы достаточно (и это могло бы быть, если бы вы отключить дженерики или вызов метода через отражение), но компилятор не страдает стиранием типа и, следовательно, знает, что соответствует только method(java.lang.Iterable).

+0

Как вы догадались в своем редактировании, мне захотелось вызвать его, используя только стирание. Я нахожу все это довольно парадоксальным, показывая, что в языке есть недостающая особенность. –

+0

@AntoineMarques вы правы. IMHO _erasure_ - это сломанная функция, или, наоборот, что-то не так, это истинные генерируемые генераторы времени выполнения. Однако это было исключено для обратной совместимости. – Thomas

1

Я не думаю, что есть способ, чтобы вызвать стертую версию статического метода (в чистой сборке)

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 
} 

Мы можем вызвать стертую версию экземпляра метод через необработанный тип

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 
} 

Назад к Статические методы. Предположим, что мы имеем это предварительно java5 код

#1 static void foo(List list){} 

#2 foo(new ArrayList()); 

теперь после Java5, # 1 обобщен в

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

но # 2 хранится как есть, с сырым типом. Как получилось, что № 2 все еще компилируется? Спектр (15.12.2.2) позволяет сделать возможным неконтролируемое преобразование. Непроверенное преобразование может преобразовать необработанный Foo в Foo<..> (но не более сложный, как Foo -> Foo<..>&Bar).

Спектр содержит только достаточные трюки, чтобы гарантировать, что устаревший код может выдержать типичное генерирование. Ваш случай, конечно, не является типичным, и трюки не применяются.

+0

+1 Очень интересно. –

+0

Не знал об этой функции. Спасибо за обмен =) –

Смежные вопросы