Как напрямую преобразовать массив объектов в массив целых чисел без изменений

У меня есть массив 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[] так же, как вы не можете привести Object (т. е. new Object()) к Integer. Вам придется создать новый массив с нужным типом компонента, а затем скопировать объекты. Или сохраните исходный массив объектов и приведите каждый элемент к целому числу при итерации (не самый безопасный вариант).

Slaw 20.06.2024 18:03
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
98
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Тот факт, что 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, если какой-либо элемент имеет неправильный тип.

ОП явно указал, что они не хотят копировать массив. (Это невозможно, но в вашем ответе должно быть именно это.)

Louis Wasserman 20.06.2024 20:22

@LouisWasserman, Мурейник уже заявил об этом, и поэтому я не почувствовал смысла копировать.

DuncG 20.06.2024 20:25

Единственный способ - создать новый массив.

Например:

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 Trunnikov 21.06.2024 11:26

@Max Если вы используете этот подход с непроверенным приведением, убедитесь, что ничто не сохраняет ссылку на список как List<Object> после публикации списка как List<Integer>. Ссылка на один и тот же список с конфликтующими общими параметризациями может привести к трудностям сужения ClassCastException.

Slaw 21.06.2024 18:38

То, что вы делаете со своим собственным Test<T> классом, можно сделать и с помощью существующих List реализаций List<Integer> list = (List)Arrays.asList(objArray);

Holger 25.06.2024 13:47

@Холгер, ты имел в виду, что это невозможно? Я получаю исключение ClassCastException при попытке привести массив Arrays.asList к другому типу T.

ipodtouch0218 25.06.2024 18:03

Вы можете привести экземпляр List так же, как и экземпляр Test, но в любом случае вы не сможете привести массив. онлайн-демо

Holger 25.06.2024 18:23

@Хольгер, ах, явное приведение к (List<Integer>) выдает, но не указывает тип (List), который не выдает? Странный.

ipodtouch0218 25.06.2024 18:24
List — сырой тип. Использование необработанных типов разрушает безопасность типов Generics (и может само по себе генерировать предупреждение компилятора). По сути, в этом случае (List) работает как сокращение от (List<Integer>) (List<?>).
Holger 25.06.2024 18:26

Другие вопросы по теме