У меня есть массив Objects, заполненный Strings:
Object[] array = new Object[count];
for (int i = 0; i < array.length; i++) {
array[i] = String.valueOf(i);
}
Затем я пополняю его Integer:
for (int i = 0; i < array.length; i++) {
array[i] = Integer.parseInt(array[i]);
}
Я хочу вернуть объект типа Integer[]. Когда я делаю:
return (Integer[]) array;
или
return Integer[].class.cast(array);
Я понимаю Exception in thread "main" java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.Integer; ([Ljava.lang.Object; and [Ljava.lang.Integer; are in module java.base of loader 'bootstrap').
Я не хочу использовать потоки, копирование массива или любые другие модификации или преобразования. Просто прямой кастинг, возможно, даже через отражение.
Есть ли варианты?




Тот факт, что Object[] заполнен экземплярами Integer, не делает его Integer[]. Вот простой способ подумать об этом: даже если он прямо сейчас наполнен Integer, ничто не мешает вам сделать array[0] = "I am not an Integer".
Короче говоря, такой состав будет невозможен. Единственный способ добиться этого — скопировать содержимое этого массива в реальный Integer[], например, с помощью потоков:
Integer[] integerArray = Arrays.stream(array).toArray(Integer[]::new);
В качестве альтернативы вы можете просто привести любой элемент, полученный из массива, перед его использованием:
for (Object o : array) {
Integer i = (Integer) o;
// Use i for something
}
Как только все они будут одного типа, вы можете сделать безопасную копию в виде массива нужного формата, используя Arrays.copy.
Примеры:
Integer[] intArray = Arrays.copyOf(array, array.length, Integer[].class);
// or
String[] strArray = Arrays.copyOf(array, array.length, String[].class);
Они выбрасывают java.lang.ArrayStoreException, если какой-либо элемент имеет неправильный тип.
ОП явно указал, что они не хотят копировать массив. (Это невозможно, но в вашем ответе должно быть именно это.)
@LouisWasserman, Мурейник уже заявил об этом, и поэтому я не почувствовал смысла копировать.
Единственный способ - создать новый массив.
Например:
Arrays.copyOf(new Object[1], 1, Integer.class);
Это не массив, но вы можете сделать это с помощью общего списка и непроверяемого приведения, используя список подстановочных знаков: List<?>. Вы даже можете использовать его, когда он содержит объекты разных типов, он выдаст ClassCastException, когда вы попытаетесь получить к ним доступ как к общему типу.
ArrayList<Object> arr = new ArrayList<Object>();
arr.add(1);
arr.add("xyz");
@SuppressWarnings("unchecked")
ArrayList<Integer> casted = (ArrayList<Integer>) (ArrayList<?>) arr;
System.out.println(casted.get(0)); // Prints 1
System.out.println(casted.get(1)); // Prints "xyz"
Integer first = casted.get(0); // Works!
// Integer second = casted.get(1); // Throws ClassCastException at runtime
// String secondStr = casted.get(1); // Doesn't compile, 'incompatible types'
String secondStrGood = (String) (Object) casted.get(1); // Works!
Это связано с тем, что ArrayLists просто используют массив Object[] вместо общего массива T[] и приводят get (вроде как...)
Если вы попытаетесь хитрить и получить внутренний массив, поддерживающий ArrayList, посредством отражения, ничего не получится. Как упоминалось ранее, это обычный массив Object[], поэтому вы можете столкнуться с той же проблемой приведения типов.
Field privateField = casted.getClass().getDeclaredField("elementData");
privateField.setAccessible(true);
// java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
Integer[] array = (Integer[]) privateField.get(casted);
Если вы попытаетесь пойти еще хитрее и попробовать этот трюк с общим классом вместо List<?>, это все равно не сработает.
public class Test<T> {
public T[] array;
}
public static void main(String args[]) {
Object[] objArray = new Object[] {1, "string"};
Test<Object> test = new Test<Object>();
test.array = objArray;
@SuppressWarnings("unchecked")
Test<Integer> testInteger = (Test<Integer>) (Test<?>) test;
// java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
Integer[] intArray = testInteger.array;
}
TL;DR: лучшее, что вы можете сделать, это использовать Collections
На самом деле это лучший ответ. Двойной кастинг - это то, что мне нужно
@Max Если вы используете этот подход с непроверенным приведением, убедитесь, что ничто не сохраняет ссылку на список как List<Object> после публикации списка как List<Integer>. Ссылка на один и тот же список с конфликтующими общими параметризациями может привести к трудностям сужения ClassCastException.
То, что вы делаете со своим собственным Test<T> классом, можно сделать и с помощью существующих List реализаций List<Integer> list = (List)Arrays.asList(objArray);
@Холгер, ты имел в виду, что это невозможно? Я получаю исключение ClassCastException при попытке привести массив Arrays.asList к другому типу T.
Вы можете привести экземпляр List так же, как и экземпляр Test, но в любом случае вы не сможете привести массив. онлайн-демо
@Хольгер, ах, явное приведение к (List<Integer>) выдает, но не указывает тип (List), который не выдает? Странный.
List — сырой тип. Использование необработанных типов разрушает безопасность типов Generics (и может само по себе генерировать предупреждение компилятора). По сути, в этом случае (List) работает как сокращение от (List<Integer>) (List<?>).
Массивы знают тип своего компонента во время выполнения. Вы не можете привести
Object[]кInteger[]так же, как вы не можете привестиObject(т. е.new Object()) кInteger. Вам придется создать новый массив с нужным типом компонента, а затем скопировать объекты. Или сохраните исходный массив объектов и приведите каждый элемент к целому числу при итерации (не самый безопасный вариант).