2016-10-04 4 views
26

Можете ли вы объяснить, почему работает следующее?Статические общие методы

public class GenericsTest<T> { 

    public void doSomething(T v1, T v2) { 

    } 

    public static <T> void doSomethingStatic(T v1, T v2) { 

    } 

    public static <T> void doSomethingStaticList(List<T> v1, List<T> v2) 
    { 

    } 

    public static void main(String[] args) { 
     GenericsTest<String> gt = new GenericsTest<>(); 

     // OK 
     gt.doSomething("abc", "abc"); 

     // Not OK 
     gt.doSomething(1, "abc"); 

     // OK 
     doSomethingStatic(1, 2); 

     // Still OK 
     doSomethingStatic(1, "abc"); 

     // So why is this not OK? 
     List<String> list1=new LinkedList<>(); 
     List<Integer> list2=new LinkedList<>(); 
     doSomethingStaticList(list1,list2); 
    } 
} 

T v1, T v2 должны быть того же типа в doSomethingStatic, но я до сих пор в состоянии передавать различные типы (целые и строки).

Если doSomethingStatic() принимает общий суперкласс по умолчанию, почему doSomethingStaticList() не работает с разными типами?

+2

В 'public static void doSomethingStatic (T v1, T v2) {', переменная типа не нужна: она семантически идентична public public void doSomethingStatic (Object v1, Object v2) {'. –

+0

К обновлению: потому что нет 'T', который бы соответствовал обоим типам. Единственным типом, который будет соответствовать обоим спискам, является тип «List», но не существует «Список ». – Hulk

+5

В случае списков, какой тип суперкласса был бы 'T', чтобы сделать их действительными? Не забывайте, что дженерики являются инвариантными, и вы не можете назначить «Список » на «Список ». –

ответ

22

В нестационарном случае вы определяете T как String при создании экземпляра GenericsTest. Следовательно, передача int даст ошибку компиляции. Если вы сделали gt.doSomething(1, 2), это тоже потерпит неудачу.

В статическом случае вы не определяете T вручную, это происходит из параметров. Это будет первый общий суперкласс обоих классов, который в этом случае равен Object. Вы можете использовать ограниченный шаблон, например. <T extends Number> или <T extends CharSequence>.

Обратите внимание, что у вас есть два различных T сек здесь:

  • GenericsTest<T>
  • public static <T> void doSomethingStatic(T v1, T v2)

Объявление общего параметра всякий раз, когда вы пишете <T>. В этом случае вы можете использовать разные буквы, чтобы избежать путаницы.

+0

'' в статическом методе полностью автономно/отделено от самого класса. Таким образом, T вернется к первому общему супертипу v1 и v2, который является объектом в этом случае. Если не указать '', то компилятор будет интерпретировать его как 'T' класса, и поскольку метод является статическим, это незаконно. –

+0

Как насчет редактирования? –

+0

@ BlueRaja-DannyPflughoeft Вопрос был отредактирован для описания другого сценария ~ через 25 минут после того, как я разместил этот ответ. Я дал короткое объяснение + ссылку в комментарии к вопросу. –

13

Это работает, потому что T в вашем статическом методе его собственного параметр типа, а не параметр T для экземпляра, который используется в вашем метода члена. Переименуйте его уточнить:

public static class GenericsTest<T> { 

    public void doSomething(T v1, T v2) { 

    } 

    public static <V> void doSomethingStatic(V v1, V v2) { 

    } 
//... 

Так что в случае doSomething(...) ваше значение параметра типа экземпляра String так это ошибка. В случае статического doSomethingStatic(...) значение параметра типа отличается:

GenericsTest.doSomethingStatic(1, "abc"); //ok 
GenericsTest.<Object>doSomethingStatic(1, "abc"); //ok 
GenericsTest.<String>doSomethingStatic(1, "abc"); //not ok 
new GenericsTest<String>().doSomething(1, "abc"); //not ok 
2

Сначала немного теории:

  • Общие методы являются методы, которые вводят свои собственные параметры типа Java Tutorials - Generic Methods
  • Тип умозаключение является Java способность компилятора смотреть на каждый вызов метода и соответствующее объявление для определения аргумента типа (или аргументов), которые делают обращение применимым Java Tutorials - Type inference

Так что случилось:

  • когда общее выражение предшествуют возвращаемое значение то новая переменная общего типа «объявлен». Таким образом, T объявления класса отличается (для компилятора) от T объявления метода.
  • компилятора применять определение типа и в вашем примере это определить, что подходящий тип применить вызов метода Object

Вы можете попробовать и этот пример без статического метода:

public class GenericsTest<T> { 

    public void doSomething(T v1, T v2) { 

    } 

    public <T> void doSomething2(T v1, T v2) { 

    } 

    public static void main(String[] args) { 
    GenericsTest<String> gt = new GenericsTest<>(); 

    // ok 
    gt.doSomething("abc", "abc"); 

    // Not ok 
    gt.doSomething(1, "abc"); 

    // ok 
    gt.doSomething2(1, 2); 

    // Still ok 
    gt.doSomething2(1, "abc"); 

    } 

} 
0

В статическом случае , вы не определяете T вручную, оно выводится из параметров. В случае doSomethingStaticList (list1, list2), где list1 является строковым общим списком, а list2 является общим списком Integer. Алгоритм вывода компилятора не сможет распознать, поскольку List и List не принадлежат к общему типу.

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