Дженерики, массивы и ClassCastException

После SDK версии iOS10 можно отправлять push-уведомления, которые могут указывать на мультимедийный файл вместе с уведомлением, заголовком и дополнительными данными.

Этот медиафайл, пусть это будет изображение, будет отображаться рядом с уведомлением, когда устройство получит уведомление.

15
задан Lahiru Ashan 28 November 2016 в 07:35
поделиться

3 ответа

Foo<Double> f = new Foo<Double>();

Когда Вы используете эту версию универсального класса Foo, затем для членской переменной a, компилятор по существу проводит эту строку:

private T[] a = (T[]) new Object[5];

и замена T с Double получить это:

private Double[] a = (Double[]) new Object[5];

Вы не можете бросить от Объекта Удвоиться, следовательно ClassCastException.

Обновление и Разъяснение: На самом деле, после выполнения некоторого тестового кода, ClassCastException является более тонким, чем это. Например, этот основной метод будет хорошо работать без любого исключения:

public static void main(String[] args) {
    Foo<Double> f = new Foo<Double>();
    System.out.println(f.getA());
}

Проблема происходит, когда Вы пытаетесь присвоиться f.getA() к ссылке типа Double[]:

public static void main(String[] args) {
    Foo<Double> f = new Foo<Double>();
    Double[] a2 = f.getA(); // throws ClassCastException
    System.out.println(a2);
}

Это вызвано тем, что информация типа о членской переменной a стирается во времени выполнения. Дженерики только обеспечивают безопасность типов во время компиляции (я так или иначе игнорировал это в своем первом сообщении). Таким образом, проблема не

private T[] a = (T[]) new Object[5];

потому что во времени выполнения этот код действительно

private Object[] a = new Object[5];

Проблема происходит когда результат метода getA(), который во времени выполнения на самом деле возвращается Object[], присвоен ссылке типа Double[] - этот оператор бросает ClassCastException, потому что Объект не может быть брошен для Удвоения.

Обновление 2: отвечать на Ваш заключительный вопрос, "почему массивы повреждают это?" Ответ - то, потому что спецификация языка не поддерживает универсальное создание массива. См. это сообщение форума для более - чтобы быть назад совместимыми, ничто не известно о типе T во времени выполнения.

20
ответ дан 1 December 2019 в 02:38
поделиться

@matt b: Спасибо за ответ! Очень полезный.

Я нашел обходное решение для заинтересованных: дайте методу геты инициализированный массив для заполнения. Тем путем информация о типе доступна.

public class Foo<T> {
  private T[] a = (T[]) new Object[5];

  public Foo() {
    // Add some elements to a
  }

  public T[] getA(T[] holdA) {
    // Check whether holdA is null and handle it...then:
    holdA = (T[]) Array.newInstance(holdA.getClass().getComponentType(), a.length);
    System.arraycopy(a, 0, holdA, 0, a.length);
    return holdA;
  }
}

Затем для Вашего основного метода:

public static void main(String[] args) {
  Foo<Double> f = new Foo<Double>();
  Double[] a2 = new Double[1];
  a2 = f.getA(a2);
}
2
ответ дан 1 December 2019 в 02:38
поделиться

В объяснении @mattb могут быть некоторые небольшие ошибки.

Ошибка не

java.lang. Объект не может быть брошен к java.lang. Дважды.

Это:

[Ljava.lang. Объект; не может быть брошен к [Ljava.lang. Дважды

[L означает массив. Таким образом, ошибка состоит в том, что массив Объектов не может быть брошен к массиву Дважды. Это - тот же случай как следующее:

Object[] oarr = new Object[10];
Double[] darr = (Double[]) oarr;

Это, очевидно, не позволяется.

Для Вашей проблемы создания безопасных с точки зрения типов массивов другая альтернатива к кроме объекта класса в init и использовании Array.newInstance:

import java.lang.reflect.Array;

class Foo<T> {
  private T[] a ;

  public Foo(Class<T> tclass) {
    a = (T[]) Array.newInstance(tclass, 5);
  }

  public T[] getA() {
    return a;
  }

  public static <T> Foo<T> create(Class<T> tclass) {
    return new Foo<T>(tclass);
  }
}

class Array1
{
  public static final void main(final String[] args) {
    Foo<Double> f = Foo.create(Double.class);
    Double[] d = f.getA();
  }


}
5
ответ дан 1 December 2019 в 02:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: