Имеется класс-обертка для параметризованного массива:
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>
, как знакомая ошибка начнёт проявляться уже прямо в конструкторе
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Реализую регистрацию и авторизацию пользователей для дальнейшего использования, с регистрацией проблем нем, все данные уходят в БД, а вот...
На сайте есть анимационный блок, анимация которого производится с применением html5, css, и jsМожно ли как то записать эту анимацию в gif? Использую...
Сейчас для того чтобы опубликовать обновление приложения в PlayМаркет, требуется API не ниже 26Иначе выдает ошибку: