Ошибка приведения Java Generics

в соответствии с заголовком, моя проблема состоит в том, чтобы получить длину массива универсального массива, который находится внутри экземпляра класса, который был инициализирован этим универсальным типом. Когда я хочу получить длину своего массива (ak.array.length), java выдает эту ошибку:

Исключение в потоке "основной" java.lang.ClassCastException: [Ljava.lang.Object; нельзя привести к [Ljava.lang.String; в Testclass.main(Testclass.java:15)

Так где же моя ошибка в этом коде? заранее спасибо

public class Testclass {

    static class ArrayKeeper<T> {
        protected T[] array;

        public ArrayKeeper() {
            array = (T[])new Object[5];
        }
    }

    public static void main(String[] args) {
        //create a new instance of inner class ArrayKepper (type String)
        Testclass.ArrayKeeper<String> ak = new Testclass.ArrayKeeper<String>();
        System.out.println(ak.array.length); //cast error here.
    }
}

person user1410707    schedule 22.05.2012    source источник


Ответы (2)


Проблема именно в этой строке:

array = (T[]) new Object[5];

Это не способ создать общий массив, вы создаете Object[], а не String[], как вы предполагали. Вы не можете создать Object[], а затем привести его к любому типу, который вы хотите, вы должны создать массив правильного класса. Попробуйте это вместо этого:

public ArrayKeeper(Class<T> clazz, int size) {
    array = (T[]) Array.newInstance(clazz, size);
}

И затем, когда вам нужно создать экземпляр ArrayKeeper, сделайте следующее:

Testclass.ArrayKeeper<String> ak =
    new Testclass.ArrayKeeper<String>(String.class, 5);
person Óscar López    schedule 22.05.2012
comment
Мех. Лучше, чем подход, основанный на отражении, просто скрыть приведения с помощью API. Посмотрите, например. реализация ArrayList в Java: она поддерживается старым простым Object[], а объекты приводятся при вызовах get(int) и тому подобного. Просто избегайте разоблачения слепков, и все будет готово. - person Louis Wasserman; 22.05.2012
comment
Отражение происходит медленно и требует от вас обхода и отслеживания множества совершенно избыточных Class объектов. Лучшим вариантом из всех, вероятно, является использование List вместо массива, но в противном случае загромождать ваш API передачей объектов Class просто неудобно. - person Louis Wasserman; 23.05.2012
comment
@LouisWasserman Отражение не так медленно, как раньше (это распространенное заблуждение), и, кроме того, как узнать, будет ли оно медленным, если вы не знаете предполагаемого использования, и еще не профилировали? В.р.т. передача объектов класса — это очень распространенная идиома, повсеместно используемая в самом Java API. - person Óscar López; 23.05.2012
comment
В каких частях Java API? Нигде в структуре коллекций, хотя и с заметным (и заслуживающим внимания) исключением EnumMap и EnumSet. Я... допускаю, что отражение происходит намного быстрее, чем раньше, и я считаю, что идиома Array.newInstance на самом деле в конечном итоге используется в AbstractCollection.toArray(T[]). Но... если вы настаиваете, вот еще один аргумент: что, если T сам по себе является обобщенным типом? Подход, который вы описываете в этом ответе, не позволяет вам использовать общий T. - person Louis Wasserman; 23.05.2012
comment
@LouisWasserman сначала прочитайте FAQ. - person Óscar López; 23.05.2012
comment
Да, я понимаю это. Но new ArrayKeeper<List<String>>(List.class, 5) выдаст ошибку ввода; он будет ожидать Class<List<String>>. В этом случае ваша единственная альтернатива — (Class) List.class, которая вызывает предупреждение о непроверенном приведении... в этом случае мы в любом случае не получаем никаких преимуществ по сравнению с подходом с оберткой API. - person Louis Wasserman; 23.05.2012
comment
В любом случае, я не вижу, как подход, основанный на отражении, добавляет какую-либо ценность. Даже если это распространенная идиома, это идиома, не имеющая никакой пользы: она просто добавляет бесполезный шаблон к тому, что может быть совершенно простым API. - person Louis Wasserman; 23.05.2012
comment
Эта статья Анжелики Лангер великолепна, но я думаю, что это раздражает, когда люди просто оставляют на нее ссылку вместо своего собственного разумного ответа. - person Paul Bellora; 23.05.2012

Я думаю, вам нужно реализовать геттер length в ArrayKeeper просто потому, что вы не можете привести Object[] к String[], а вызов ak.array действительно пытается выполнить это приведение.

person Louis Wasserman    schedule 22.05.2012
comment
Насколько я понимаю: не array = (T[])new Object[5]; сделать бросок уже? - person Maximilian Hils; 22.05.2012
comment
Неа. Из-за стирания типа во время выполнения в этот момент фактическое приведение типов не происходит. (После стирания типа T становится Object.) - person Louis Wasserman; 22.05.2012