2015-04-08 1 views
1

Может anybode exlain почему родовое <Object[]> вызывает ClassCastException (RuntimeException!)Дженерики как результат java.lang.Object не может быть приведен в [Ljava.lang.Object

Я знаю, что все дженерики удаление то время как фаза компиляции и сделать не влияют на байт-код. Но, похоже, у него есть какой-то нюанс.

Вот мой пример (упрощенный для этой должности):

public class CastTest { 
    public static void main(String[] args) { 
     List a = new ArrayList(); 
     a.add(new Object()); 
     List<Object[]> b = a; 
     b.get(0).toString(); 
    } 
} 

этот код возвращает:

Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to [Ljava.lang.Object; 
    at CastTest.main(CastTest.java:9) 

Я не понимаю, что не так с этим кодом. Может ли кто-нибудь объяснить это поведение?

+5

Потому что 'a' содержит' Object', а не 'Object []'. – Maroun

+0

Это не матер, я не использовал какие-либо приведения в этом примере – Akvel

+0

Это неявно сделано. – Maroun

ответ

2

Вы сообщаете компилятору, что хотите ll Object[].toString(). Вот почему компилятор генерирует бросок (checkcast):

0: new   #2     // class java/util/ArrayList 
3: dup 
4: invokespecial #3     // Method java/util/ArrayList."<init>":()V 
7: astore_1 
8: aload_1 
9: new   #4     // class java/lang/Object 
12: dup 
13: invokespecial #1     // Method java/lang/Object."<init>":()V 
16: invokeinterface #5, 2   // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 
21: pop 
22: aload_1 
23: astore_2 
24: aload_2 
25: iconst_0 
26: invokeinterface #6, 2   // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 
31: checkcast  #7     // class "[Ljava/lang/Object;" 
34: invokevirtual #8     // Method java/lang/Object.toString:()Ljava/lang/String; 
37: pop 
38: return 

Вы можете предотвратить байткод бросить, добавив бросание себя в коде Java:

public static void main(String[] args) { 
    List a = new ArrayList(); 
    a.add(new Object()); 
    List<Object[]> b = a; 
    ((Object) b.get(0)).toString(); 
} 

Теперь компилятор видит, что приведение к Object[] не требуется, поскольку вам нужна только ссылка Object.checkcast опкод опущено:

0: new   #2     // class java/util/ArrayList 
3: dup 
4: invokespecial #3     // Method java/util/ArrayList."<init>":()V 
7: astore_1 
8: aload_1 
9: new   #4     // class java/lang/Object 
12: dup 
13: invokespecial #1     // Method java/lang/Object."<init>":()V 
16: invokeinterface #5, 2   // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 
21: pop 
22: aload_1 
23: astore_2 
24: aload_2 
25: iconst_0 
26: invokeinterface #6, 2   // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 
31: invokevirtual #7     // Method java/lang/Object.toString:()Ljava/lang/String; 
34: pop 
35: return 
+0

Большое спасибо !!!! – Akvel

0

Почему вы выбрали его в списке массива объектов.

Попробуйте список объектов, и он будет работать.

import java.util.ArrayList; 
import java.util.List; 

public class CastTest { 
    public static void main(String[] args) { 
     List a = new ArrayList(); 
     a.add(new Object()); 
     List<Object> b = a; 
     b.get(0).toString(); 
    } 
} 
+0

Почему - это еще один вопрос, давайте соглашаемся, что эта броска нужна по какой-то причине :-) Я понимаю, что если я удалю , все будет хорошо работать. – Akvel

2

Может быть, это яснее, если вы посмотрите на это так:

import java.util.ArrayList; 
import java.util.List; 

class Dog { } 
class Cat { } 

public class CastTest { 
    public static void main(String[] args) { 
     List a = new ArrayList(); 
     a.add(new Dog()); 
     List<Cat> b = a; 
     Cat c = b.get(0); 
    } 
} 

$ java CastTest 
Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat 
    at CastTest.main(CastTest.java:12) 

Если факт, что это не так, что дженерики не влияют на байт-код. Если вы используете javap увидеть байт-код для выше, вы можете увидеть, что бросок генерируется, чтобы убедиться, объект действительно Cat перед выполнением задания:

... 
    26: invokeinterface #7, 2   // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 
    31: checkcast  #8     // class Cat 
    34: astore_3  
    35: return 

Если вы действительно хотите, чтобы это было Список массивов объектов, вы должны добавить массив объектов:

import java.util.ArrayList; 
import java.util.List; 

public class CastTest { 
    public static void main(String[] args) { 
     List a = new ArrayList(); 
     a.add(new Object[]{}); 
     List<Object[]> b = a; 
     System.out.println(b.get(0).toString()); 
    } 
} 

$ java CastTest 
[Ljava.lang.Object;@65685e30 
+0

Это решение, но не ответ на вопрос, почему JVM попробовал лить элемент в списке – Akvel

+1

Привет, @Akvel, см. Мое последнее редактирование, чтобы увидеть, что актеры генерируются в байтовом коде, как описано в приведенной выше ссылке (docs.oracle. com/javase/tutorial/java/generics/erasure.html), где говорится, что компилятор будет «Вставлять типы приведения в случае необходимости для сохранения безопасности типов». – jas

2

Как уже говорили другие, потому что Object не Object[]

Существует намек на фразу java.lang.Object cannot be cast to [Ljava.lang.Object

Согласно JNI types and Data Structures, [Ljava.lang.Object означает:

  • [ - массив
  • L - класс

Так java.lang.Object cannot be cast to [Ljava.lang.Object может быть прочитана как объект не может быть приведен к массив Object

0

Это потому, что компилятор разбивает

b.get(0).toString(); 

вниз в

Object[] temp = b.get(0); 
    temp.toString(); 

, который имеет оттенок Object[], когда стирается.

Теперь, возможно, компилятор может также выбрать разбить его на

Object temp = b.get(0); 
    temp.toString(); 

, так как ссылка используется только для вызова .toString() который объявлен на Object. И если бы это было так, это бы избегало приведения. Однако почему компилятор поставил бы это дополнительное усилие анализа, чтобы что-то сделать по-другому, когда это будет иметь значение только в том случае, если вы используете неправильные типы?