Имеется класс-обертка для параметризованного массива:
public class CatContainer <T> {
T[] names;
public CatContainer(){
names=(T[]) new Object[10];
}
// void set(int index, T value){
// names[index]=value;
// }
}
Насколько я понимаю, если создать экземпляр параметризованного класса следующем образом: CatContainer<String> cats=new CatContainer<>() то ссылка cats будет указывать на объект со следующей структурой:
public class CatContainer {
Object[] names;
public CatContainer(){
names= new Object[10]; // массив не может изменить
//свой тип, приведение типов отбрасывается
}
// void set(int index, Object value){
// names[index]=value;
// }
}
Но почему же тогда при попытке обратиться к массиву с типом Object[] и записать туда элемент типа String следующим образом cats.names[0]="Murzik"; возникает ошибка:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
P.S. Если обратиться к массиву через метод void set(int index, T value) следующим образом: cats.set(0, "Murzik") то ошибки приведения типов не возникает
P.P.S Вот код класса - клиента:
public class GenericsArray {
public static void main(String[] args){
CatContainer<String> cats=new CatContainer<>();
cats.names[0]="Murzik"; //ClassCastException
cats.set(0, "Murzik"); // OK
Object[] arr=new Object[10];
arr[0]="Hello"; //OK
}
}
Дело не в присвоении, сообщение об ошибке говорит о том, что массив типа Object ([Ljava.lang.Object) не может быть преобразован в массив типа String ( [Ljava.lang.String).
Проблему можно воспроизвести и без присвоения, просто обратившись к элементу массива.
CatContainer<String> cats=new CatContainer<>();
System.out.println(cats.names[0]);
либо выполнив безобидную, казалось бы, операцию:
System.out.println(((String[])cats.names));
Ошибка возникает, т.к. при приведении проверяется реальный тип объекта-массива. Это делается из-за того, что в общем случае нельзя безопасно привести массив одного типа к массиву другого типа из-за связанных с такой операцией проблем:
Object[] objects = new Object[10];
objects[0] = new Integer(2);
//здесь возникнет ClassCastException
String[] strings = (String[]) objects;
//иначе возникли бы странные проблемы здесь
System.out.println(strings[0].length());
Если реальный тип массива совпадает, то приведение сработает нормально.
Object[] objects = new String[10];
//ОК
String[] strings = (String[]) objects;
//а вот здесь уже ArrayStoreException
objects[0] = new Integer(2);
Внутри обобщенного класса T буквально заменяется на Object, присвоения и приведения типов работают. Но в вызывающем коде используется конкретный тип со всеми вытекающими приведениями. Например, этот код:
CatContainer<String> cats=new CatContainer<>();
System.out.println(cats.names[0]);
Фактически заменяется на:
CatContainer cats=new CatContainer();
System.out.println(((String[]) cats.names)[0]);
Пути выхода из сложившейся ситуации:
принимайте объект класса или массива и создавайте массив нужного типа через Array.newInstance:
import java.lang.reflect.*;
...
//конструктор
public CatContainer(Class<T> c){
names=(T[]) Array.newInstance(c, 10);
}
...
//пример работы с объектом
CatContainer<String> cats=new CatContainer<>(String.class);
cats.names[0] = "Murzik";
System.out.println(cats.names[0]);
используйте вместо массива коллекцию, например, ArrayList.
Смотрите также:
names представляет собой Object[]cats.names - это попытка привести Object[] (реальный тип names) к String[] (generic-тип)T[] names заменяется на Object[] names, а его вызовы - на (T[])names. В данном случае - на ((String[])cats.names)В случае же метода set всё работает нормально, так как там нет приведений типа: вся работа ведётся напрямую с Object[], как и указано во втором примере кода (условный вид кода после type erasure)
Код names = (T[])new Object[10]; в конструкторе CatContainer, кстати, тоже представляет собой "опасную" конструкцию: покуда T ничем не ограничен (то есть T extends Object) всё нормально: Object[] успешно приводится к Object[]. Но стоит сделать, например, <T extends String>, как знакомая ошибка начнёт проявляться уже прямо в конструкторе
Современные инструменты для криптотрейдинга: как технологии помогают принимать решения
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости