java.lang.Object 的泛型不能转换为 [Ljava.lang.Object
Generics as result of java.lang.Object cannot be cast to [Ljava.lang.Object
任何人都可以解释为什么泛型 <Object[]>
会导致 ClassCastException
(RuntimeException!)
我知道在编译阶段删除所有泛型并且对字节码没有任何影响。但它似乎有一些细微差别。
这是我的例子(为此post做了简化):
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();
}
}
此代码 returns:
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to [Ljava.lang.Object;
at CastTest.main(CastTest.java:9)
我不明白这段代码有什么问题。有人可以解释这种行为吗?
为什么要将其转换为对象数组列表。
尝试对象列表,它会起作用。
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();
}
}
也许这样看会更清楚:
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
正如其他人所说,因为 Object
不是 Object[]
短语中有提示java.lang.Object cannot be cast to [Ljava.lang.Object
根据JNI types and Data Structures,[Ljava.lang.Object
表示:
[
- 数组
L
- 一个 Class
所以java.lang.Object cannot be cast to [Ljava.lang.Object
可以读作对象不能转换为对象数组
您是在告诉编译器您要调用 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
这是因为编译器中断了
b.get(0).toString();
向下进入
Object[] temp = b.get(0);
temp.toString();
擦除后转换为 Object[]
。
现在可以说,编译器可以也选择将其分解为
Object temp = b.get(0);
temp.toString();
因为引用仅用于调用在 Object
上声明的 .toString()
。如果它这样做了,它就会避免强制转换。但是,为什么编译器会投入额外的分析工作以不同的方式做某事,而只有当你使用错误的类型时才重要?
任何人都可以解释为什么泛型 <Object[]>
会导致 ClassCastException
(RuntimeException!)
我知道在编译阶段删除所有泛型并且对字节码没有任何影响。但它似乎有一些细微差别。
这是我的例子(为此post做了简化):
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();
}
}
此代码 returns:
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to [Ljava.lang.Object;
at CastTest.main(CastTest.java:9)
我不明白这段代码有什么问题。有人可以解释这种行为吗?
为什么要将其转换为对象数组列表。
尝试对象列表,它会起作用。
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();
}
}
也许这样看会更清楚:
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
正如其他人所说,因为 Object
不是 Object[]
短语中有提示java.lang.Object cannot be cast to [Ljava.lang.Object
根据JNI types and Data Structures,[Ljava.lang.Object
表示:
[
- 数组L
- 一个 Class
所以java.lang.Object cannot be cast to [Ljava.lang.Object
可以读作对象不能转换为对象数组
您是在告诉编译器您要调用 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
这是因为编译器中断了
b.get(0).toString();
向下进入
Object[] temp = b.get(0);
temp.toString();
擦除后转换为 Object[]
。
现在可以说,编译器可以也选择将其分解为
Object temp = b.get(0);
temp.toString();
因为引用仅用于调用在 Object
上声明的 .toString()
。如果它这样做了,它就会避免强制转换。但是,为什么编译器会投入额外的分析工作以不同的方式做某事,而只有当你使用错误的类型时才重要?