2009-04-23 2 views
3

Я запутался следующий код:Смущает Java генериков, требующих слепок

import java.util.ArrayList; 
import java.util.LinkedList; 
import java.util.List; 

public class GenericsTest<T extends List> { 

    public void foo() { 
    T var = (T) new LinkedList(); 
    } 

    public static void main(String[] args) { 
     GenericsTest<ArrayList> gt1 = new GenericsTest<ArrayList>(); 
     gt1.foo(); 
     System.out.println("Done"); 
    } 
} 

Тип времени выполнения T, как представляется, быть java.util.List, независимо от того, какой тип параметра я передал в конструктор.

Итак, почему компилятор требует отливки до T при назначении var? Должна ли она знать во время компиляции, что LinkedList можно назначить List?

Я понимаю, что код является фиктивным, и я понимаю, почему он работал во время выполнения, хотя похоже, что это не так. Часть, которая меня смущает, почему компилятор требует, чтобы я набирал (T) при выполнении задания? Тем не менее, он отлично компилируется без фиктивного литья.

Предположительно, компилятор понимает стирание. Похоже, что компилятор должен иметь возможность компилировать код без приведения.

ответ

7

В комментарии плакат спрашивает,

Однако, по-видимому, компилятор знает, что так почему он должен иметь оттенок (T). Есть ли какой-либо возможный способ: листинг может провалиться?

Этот литой не подведет. Но компилятор предупреждает о том, что этот код устанавливает время, в которое разгоняется бомба замедленного действия где-то еще с ClassCastException.

В этом примере нет оснований использовать генераторы, поскольку ни один из API не использует переменную типа T. Посмотрите на более реалистичное применение дженериков.

public class GenericsTest<T extends List> { 

3 public T foo() { 
4  T var = (T) new LinkedList(); 
5  return var; 
6 } 

8 public static void main(String... argv) { 
9  GenericsTest<ArrayList> gt1 = new GenericsTest<ArrayList>(); 
10  gt1.foo(); 
11  System.out.println("Test one okay"); 
12  ArrayList<?> list = gt1.foo(); 
13  System.out.println("Test two okay"); 
14 } 

    } 

ClassCastException брошен на линии 12. ClassCastException без гипса? Вызывающий код совершенно корректен. Недопустимый листинг, ошибка, находится в строке 4 в вызываемом методе. Но исключение возникает в какое-то время и далеко.

Целью Java-дженериков является обеспечение безопасности кода. Если все кода было скомпилировано без предупреждений «unchecked», гарантия заключается в том, что во время выполнения не будет ClassCastException. Однако, если библиотека, на которую вы полагаетесь, была написана неправильно, как и в этом примере, обещание нарушено.

0

Я предположил, что вы пришли с C#? В этом случае Java-дженерики не являются генераторами C#.

Java не генерирует строго типизированный класс при предоставлении параметра типового типа, он просто использует наименее распространенный тип деноминации, который будет работать для всех возможных комбинаций параметров универсального типа, которые могут быть предоставлены.

Таким образом, в этом случае используется List, так как вы указываете extends List.

1

Это из-за type erasure; во время компиляции общий параметр превращается во все, что имеет его нижняя граница.

Компилятор пытается сказать вам, что что-то не так. Если бы он сделал то, что, по вашему мнению, должен был, вы получите ClassCastException - вы не можете наложить LinkedList на ArrayList.

+1

Спасибо за ссылку. Это объясняет, почему тип среды выполнения List ... и я не получаю исключение ClassCastException. Однако, по-видимому, компилятор знает, что так зачем нужно, чтобы приведение к (T). Есть ли какой-нибудь возможный способ, когда бросок может провалиться? –

11

LinkedList присваивается List, но он не может быть назначен T.

В некотором смысле ваш тестовый код не должен работать - он работает только потому, что приведение эффективно удаляется компилятором во время выполнения (из-за стирания типа). Это полезно во время компиляции, потому что требуется, чтобы вы (разработчик) обещали компилятору, что вы делаете что-то действительное, которое он не может проверить. Так как это происходит, то, что вы делаете , не соответствует, но компилятор не знает об этом, и нет возможности проверить его во время выполнения без дополнительной информации. Вы можете предоставить эту информацию, хотя:

import java.util.ArrayList; 
import java.util.LinkedList; 
import java.util.List; 

public class GenericsTest<T extends List> { 

    Class<T> clazz; 

    public GenericsTest(Class<T> clazz) { 
    this.clazz = clazz; 
    } 

    public void foo() { 
    T var = clazz.cast(new LinkedList()); 
    } 

    public static void main(String[] args) { 
    GenericsTest<ArrayList> gt1 = 
     new GenericsTest<ArrayList>(ArrayList.class); 
    gt1.foo(); 
    System.out.println("Done"); 
    } 
} 

Это теперь будет терпеть неудачу с соответствующим исключением - информация о типе еще доступна во время выполнения, и мы используем его, чтобы выполнить бросок в безопасном способе.

Обратите внимание, что если вы скомпилируете подходящие предупреждения, компилятор скажет вам, что ваш исходный код на самом деле ничего не проверял.

Для получения дополнительной информации об этом и многом другом, посмотрите на довольно всеобъемлющий Java Generics FAQ.

+0

Я пытался ссылаться на FAQ Анжелики Лангер, но у него есть эта досадная тенденция замораживать IE7 - каждый раз, когда я пытаюсь это сделать. –

+0

Странно. Просто вы можете использовать Chrome, хотя бы:) –

+0

Спасибо за ответ. Я понимаю, что код был фиктивным и не должен работать. Я не могу понять, почему компилятор дает ошибку, если (T) удаляется, хотя, как вы указали, (T) бессмысленно и всегда будет работать. –

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