2014-06-01 2 views
0

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

class MyList<T> 
{ 
    private Class<T> genericClass; 
    private List<T> list = new ArrayList<>(); 

    public MyList(Class<T> genericClass) 
    { 
     this.genericClass = genericClass; 
    } 

    public void add(T elem) 
    { 
     list.add(elem); 
    } 

    //... 

    public Class<T> getGenericParamClass() 
    { 
     return genericClass; 
    } 
} 

class A{} 
class B extends A{} 
class C extends A{} 

class Program 
{ 
    public static void main(String... args) 
    { 
     MyList<B> list1 = new MyList<>(B.class); 
     MyList<C> list2 = new MyList<>(C.class); 

     MyList<? extends A> ls = checkStuff() ? list1 : list2; 

     ls.add(ls.getGenericParamClass().cast(lotsOfStuff())); //ERROR ?!! 
    } 

    static boolean checkStuff() 
    { 
     Random random = new Random(); 
     return random.nextBoolean(); 
    } 

    static A lotsOfStuff() 
    { 
     return new B(); 
    } 
} 

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

я мог бросить дженерики из окна, идут полным бесконтрольно и просто сказать:

A val = lotsOfStuff(); 
if (myList.getGenericParamClass().isInstance(val)) 
    ls.add(val) 
else 
    throw new SomeException(); 

Но, что, вероятно, создаст больше проблем, чем решить, а также это было бы действительно ошибка меня.

Я пропустил что-то здесь, или это просто невозможно, как я думал об этом?

Edit: Я полностью понимаю, а почему-то, как это не может работать:

List<? extends Number> abc=new ArrayList<Integer>(); 
abc.add(new Integer(10)); 

Но на мой взгляд, следующие транзитивности: тип параметра оных() Is-The-Same - как параметр типа MyList Is-The-Same - как параметр типа класса, возвращаемого getGenericParamClass() Is-The-Same-как возвращаемый тип метода cast() этого класса. Я (как человек) может знать, что эти неизвестные типы одинаковы, потому что я получаю их от одного и того же объекта.

Есть ли ошибка в моей логике или это ограничение Java?

+1

не буду [ 'Коллекция. checkedList'] (http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#checkedList (java.util.List,% 20java.lang.Class)) сделать это из коробка? –

+0

Попробуйте этот простой код 'List abc = new ArrayList (); abc.add (новый Integer (10)); 'и затем прочитайте его здесь [Когда для генерирования Java требуется вместо и есть ли недостатки переключения?] (http://stackoverflow.com/questions/897935/when -do-ява-генерики-требует, распространяется трет-вместо-о-т-и--там любой вниз) – Braj

ответ

2

Ошибка компиляции:

Метод дополнения (захват # 2-в Расширяет?) В типе MyList не применяется для аргументов

(захват # 3-в Расширяет?)

Чтобы понять это сообщение, напомните, что ? extends A обозначает неизвестный тип, являющийся подтипом A. Это компилятор не может знать, что ? extends A возвращаемого lotsOfStuff() такого же (или подтип) ? extends A, что ожидает, что метод MyList.add, а на самом деле, ваша программа не гарантирует, что это так (потому что lotsOfStuff() всегда возвращает B, даже если его следует добавить в список C.)

Чтобы выразить, что эти два типа одного типа, мы должны использовать параметр типа. Самый простой способ, чтобы получить один, чтобы переместить код делает кастинг и метание в класс MyList<T> (который уже имеет соответствующий параметр типа), например, путем добавления следующего метода:

void addOrThrow(Object o) { 
    add(genericClass.cast(o)); 
} 
Смежные вопросы