Object[] o = {"2","sd"};
String[] s = (String[])o;//编译成功,但运行时报错
然后因为能编译成功,我们看看反编译的结果
6: ldc #3 // String 2
8: aastore
9: dup
10: iconst_1
11: ldc #4 // String sd
13: aastore
14: astore_1
15: aload_1
16: checkcast #5 // class "[Ljava/lang/String;"
19: checkcast #5 // class "[Ljava/lang/String;"
上面两个checkcast,表明运行时会进行(String[])o:Object[]转String[]的类型判断,以及String[] s = (String[])o:String[]转String[]的类型判断,最后运行时类型转换报错
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;at ConvertTest.main(ConvertTest.java:7)
但是到了泛型里面 似乎又可以转换了,这里让人百思不得其解,跪求大神指点。情况如下:
public static void main(String[] args) {
// Object[] o = {"2","sd"};
// String[] s = (String[])o;
String[] i = {"1","2"};
String[] s1 = getElement(i);//成功运行
getElement(i).getClass();//返回String[]类型
}
public static <T> T[] getElement(T[] obj){
return obj;
}
我写了一个泛型方法,由于泛型擦除,最后getElement()方法返回的肯定是Object[](接下来贴反编译文件,也能证明),然后却成功的转换成了String[]类型,这里好奇怪,不理解。
24: invokestatic #5 // Method getElement:([Ljava/lang/Object;)[Ljava/lang/Object;
//这里调用getElement方法,返回类型为Ljava/lang/Object即Object[]
27: checkcast #6 // class "[Ljava/lang/String;"
/这里也会在运行进行类型转换判断,但结果是运行过去了
30: invokevirtual #7 // Method java/lang/Object.getClass:()Ljava/lang/Class;
public static <T extends java/lang/Object> T[] getElement(T[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: areturn
LineNumberTable:
line 13: 0
Signature: #20 // <T:Ljava/lang/Object;>([TT;)[TT;
真的不理解,同样有checkcast,一个运行时出错,一个运行通过,我到底哪里想错了,求高人指点
18 个解决方案
#1
举例不恰当。
泛型方法public static <T> T[] getElement(T[] obj),当你传入的参数为String[] i 时,此时的T就为String了,也就是方法可以理解为:public static String[] getElement(String[] obj)。
你直接这样调用就会编译失败了:String[] s1 = getElement(o);
泛型方法public static <T> T[] getElement(T[] obj),当你传入的参数为String[] i 时,此时的T就为String了,也就是方法可以理解为:public static String[] getElement(String[] obj)。
你直接这样调用就会编译失败了:String[] s1 = getElement(o);
#2
但是泛型类型擦除过后,T就直接成Object了,所以函数返回类型就是Object[],而且反编译:
24: invokestatic #5 // Method getElement:([Ljava/lang/Object;)[Ljava/lang/Object;
也可以看出返回类型是[Ljava/lang/Object;不能理解为public static String[] getElement(String[] obj)吧?
“当你传入的参数为String[] i 时,此时的T就为String了”版主,能这样理解吗?有依据没?不理解。
24: invokestatic #5 // Method getElement:([Ljava/lang/Object;)[Ljava/lang/Object;
也可以看出返回类型是[Ljava/lang/Object;不能理解为public static String[] getElement(String[] obj)吧?
“当你传入的参数为String[] i 时,此时的T就为String了”版主,能这样理解吗?有依据没?不理解。
#3
我只知道向下转型会出问题,猜测应该是将o看出了一个对象没有主动拆分,因为不用数字的话向下转又没错,个人意见。
#4
我试了下,及时Object不是数组转型成String[]也是不会报错的
#5
因为 Java 在编译的时候,根本就不会在意你的具体类型,只要你进行一次强制类型转换, Java 就默认你已经知道他的正确类型了
比如说,有一个类 A, 继承了 B,C 两种接口,然后我们令 B b = new A();那么如果希望使用到 b 的 C 接口的方法,我们就得进行类型转换 C c = (C) b;但是 B,C 之间又没有明确的继承关系,所以Java 为了避免这种情况,就允许强制类型转换不正确的类型.
只有当你具体运行到代码的时候,才会根据实际类型抛出异常
比如说,有一个类 A, 继承了 B,C 两种接口,然后我们令 B b = new A();那么如果希望使用到 b 的 C 接口的方法,我们就得进行类型转换 C c = (C) b;但是 B,C 之间又没有明确的继承关系,所以Java 为了避免这种情况,就允许强制类型转换不正确的类型.
只有当你具体运行到代码的时候,才会根据实际类型抛出异常
#6
说实话,没怎么懂楼主的意思。囧。现在的关键点是在泛型擦除后,泛型方法返回的是Object类型还是编译器已经帮忙自动强转成我们要的类型?
#7
因为 Java 在编译的时候,根本就不会在意你的具体类型,只要你进行一次强制类型转换, Java 就默认你已经知道他的正确类型了
比如说,有一个类 A, 继承了 B,C 两种接口,然后我们令 B b = new A();那么如果希望使用到 b 的 C 接口的方法,我们就得进行类型转换 C c = (C) b;但是 B,C 之间又没有明确的继承关系,所以Java 为了避免这种情况,就允许强制类型转换不正确的类型.
只有当你具体运行到代码的时候,才会根据实际类型抛出异常
#8
虽然说楼主的回答并没能解答我上面问题的疑惑,但楼主提到的这种运行处类型转换错误的原因真心受教了,的确这是个值得注意的点。
因为 Java 在编译的时候,根本就不会在意你的具体类型,只要你进行一次强制类型转换, Java 就默认你已经知道他的正确类型了
比如说,有一个类 A, 继承了 B,C 两种接口,然后我们令 B b = new A();那么如果希望使用到 b 的 C 接口的方法,我们就得进行类型转换 C c = (C) b;但是 B,C 之间又没有明确的继承关系,所以Java 为了避免这种情况,就允许强制类型转换不正确的类型.
只有当你具体运行到代码的时候,才会根据实际类型抛出异常
你确定范型擦除了数据了吗?擦除数据是当数组形式为 LIst<T>[]时才会将类型擦除,你直接使用 String[]类型是保留的,怎么会擦除类型呢
#9
虽然说楼主的回答并没能解答我上面问题的疑惑,但楼主提到的这种运行处类型转换错误的原因真心受教了,的确这是个值得注意的点。
因为 Java 在编译的时候,根本就不会在意你的具体类型,只要你进行一次强制类型转换, Java 就默认你已经知道他的正确类型了
比如说,有一个类 A, 继承了 B,C 两种接口,然后我们令 B b = new A();那么如果希望使用到 b 的 C 接口的方法,我们就得进行类型转换 C c = (C) b;但是 B,C 之间又没有明确的继承关系,所以Java 为了避免这种情况,就允许强制类型转换不正确的类型.
只有当你具体运行到代码的时候,才会根据实际类型抛出异常
你确定范型擦除了数据了吗?擦除数据是当数组形式为 LIst<T>[]时才会将类型擦除,你直接使用 String[]类型是保留的,怎么会擦除类型呢
#10
你的类型转换不是通过擦除得到的,范型都会有类似的类型转换,但是擦除和这种类型转换是不一样的,
当你使用范型的时候, java 会记录你的数据类型,当操作完成,再强制转换为之前应该有得类型,但这个不叫做擦除
当你使用范型的时候, java 会记录你的数据类型,当操作完成,再强制转换为之前应该有得类型,但这个不叫做擦除
#11
你的类型转换不是通过擦除得到的,范型都会有类似的类型转换,但是擦除和这种类型转换是不一样的,
当你使用范型的时候, java 会记录你的数据类型,当操作完成,再强制转换为之前应该有得类型,但这个不叫做擦除
没错,擦除后返回的是Object[],然后java记录下我的数据类型是String[],然后将Object[]转成String[],这就是我疑问的地方,因为和我问题开始提到的这种类型是会在运行时失败矛盾了。
难道java自动转换就可以成功,而我们认为转换就会失败?
#12
不一样的,从 String[]到 Object[]你可以认为是向上转型,虽然不能这么说,但可以这么想,然后, Object[]向 String[]转型是向下转型,可能会失败的.
而你第一个例子中, o 是一个 Object[]类型的数组,你将它强制向下转型为 String[],但是他的真实类型并不是 String[],所以运行时错误
而对于第二个例子,他的真实类型是 String[],先向上转型成功,然后再向下转型,真实类型是对的,也成功
而你第一个例子中, o 是一个 Object[]类型的数组,你将它强制向下转型为 String[],但是他的真实类型并不是 String[],所以运行时错误
而对于第二个例子,他的真实类型是 String[],先向上转型成功,然后再向下转型,真实类型是对的,也成功
#13
其他的我不太了解,我也是只知道,在java中子类可以强转成父类,但是反过来是有问题的
Public class A extends B{
}
A a = new A();
B b = new B();
B b = a;
Public class A extends B{
}
A a = new A();
B b = new B();
B b = a;
#14
自己顶起来。有高人意见不?现在只能模模糊糊的认为编译器会记住擦除前的具体类型,然后擦除后返回的Object或Object[]类型再由编译器自动转成之前记住的具体类型。。这是书上通俗的说法,但具体是怎么实现的?看反编译的文件也看不出来呀。
#15
擦除确实是擦除了,应该是编译的时候是擦除,你所使用的泛型成了object,但是在你运行的时候传入的是String,所有返回也是String也很正常
楼主比较纠结的问题是你看了编译文件,不正事编译时,擦除了吗
楼主比较纠结的问题是你看了编译文件,不正事编译时,擦除了吗
#16
擦除确实是擦除了,应该是编译的时候是擦除,你所使用的泛型成了object,但是在你运行的时候传入的是String,所有返回也是String也很正常
楼主比较纠结的问题是你看了编译文件,不正事编译时,擦除了吗

特地翻阅了英文版的《core java》,按书上的意思是,编译器会插入一个cast,来帮助我们强转,也就是说,字节码文件里面会根据你传入的参数类型生成强转的指令,而不是运行时强转的,运行时仅仅是checkcast一次,判断String[](引用的声明类型)和String[](编译器帮忙强转出来的类型)是否可以转换。
String[] s1 = getElement(i);
声明的类型是String[],即引用s1的类型
运行时右边方法传过去的类型也是String[],因为编译器帮忙强转了
可是,现在纠结的是,我反编译的代码里面看不到编译器插入的cast指令呀!所以不能验证书上的说法。所以头疼这个问题,现在不管了,就相信书上书法吧。
#17
我觉得是分配内存时的问题
也就是测试的问题 你那个方法是不同的
一个是
另一个是
也就是测试的问题 你那个方法是不同的
一个是
Object[] xxx = new Object[] {'"xxx", "yyy"};这个转换成String[] 就会失败 因为分配内存是按Object分的
另一个是
Object[] xxx = new String[] {"xxx", "yyy"};这个就是对的 因为分配内存时布局就是String
#18
第一个声明是Object[] ,然后向下转型为String[],而实际上是Object[] 所以报错。虽然java的数组是协变的,但是编译器不会推断Object数组内部的数据类型。
第二个声明为String[] ,对编译时期来说,编译器是知道你的类型是String[] 的,泛型的擦除跟楼主的问题没有根本上的联系,不信你可以直接声明数据类型自己强制转换。
在运行期,Object[] 引用的数据本身就是String[],所以是正确的强制转换。
第二个声明为String[] ,对编译时期来说,编译器是知道你的类型是String[] 的,泛型的擦除跟楼主的问题没有根本上的联系,不信你可以直接声明数据类型自己强制转换。
在运行期,Object[] 引用的数据本身就是String[],所以是正确的强制转换。
#1
举例不恰当。
泛型方法public static <T> T[] getElement(T[] obj),当你传入的参数为String[] i 时,此时的T就为String了,也就是方法可以理解为:public static String[] getElement(String[] obj)。
你直接这样调用就会编译失败了:String[] s1 = getElement(o);
泛型方法public static <T> T[] getElement(T[] obj),当你传入的参数为String[] i 时,此时的T就为String了,也就是方法可以理解为:public static String[] getElement(String[] obj)。
你直接这样调用就会编译失败了:String[] s1 = getElement(o);
#2
举例不恰当。
泛型方法public static <T> T[] getElement(T[] obj),当你传入的参数为String[] i 时,此时的T就为String了,也就是方法可以理解为:public static String[] getElement(String[] obj)。
你直接这样调用就会编译失败了:String[] s1 = getElement(o);
24: invokestatic #5 // Method getElement:([Ljava/lang/Object;)[Ljava/lang/Object;
也可以看出返回类型是[Ljava/lang/Object;不能理解为public static String[] getElement(String[] obj)吧?
“当你传入的参数为String[] i 时,此时的T就为String了”版主,能这样理解吗?有依据没?不理解。
#3
我只知道向下转型会出问题,猜测应该是将o看出了一个对象没有主动拆分,因为不用数字的话向下转又没错,个人意见。
#4
我只知道向下转型会出问题,猜测应该是将o看出了一个对象没有主动拆分,因为不用数字的话向下转又没错,个人意见。
#5
因为 Java 在编译的时候,根本就不会在意你的具体类型,只要你进行一次强制类型转换, Java 就默认你已经知道他的正确类型了
比如说,有一个类 A, 继承了 B,C 两种接口,然后我们令 B b = new A();那么如果希望使用到 b 的 C 接口的方法,我们就得进行类型转换 C c = (C) b;但是 B,C 之间又没有明确的继承关系,所以Java 为了避免这种情况,就允许强制类型转换不正确的类型.
只有当你具体运行到代码的时候,才会根据实际类型抛出异常
比如说,有一个类 A, 继承了 B,C 两种接口,然后我们令 B b = new A();那么如果希望使用到 b 的 C 接口的方法,我们就得进行类型转换 C c = (C) b;但是 B,C 之间又没有明确的继承关系,所以Java 为了避免这种情况,就允许强制类型转换不正确的类型.
只有当你具体运行到代码的时候,才会根据实际类型抛出异常
#6
我试了下,及时Object不是数组转型成String[]也是不会报错的
我只知道向下转型会出问题,猜测应该是将o看出了一个对象没有主动拆分,因为不用数字的话向下转又没错,个人意见。
#7
因为 Java 在编译的时候,根本就不会在意你的具体类型,只要你进行一次强制类型转换, Java 就默认你已经知道他的正确类型了
比如说,有一个类 A, 继承了 B,C 两种接口,然后我们令 B b = new A();那么如果希望使用到 b 的 C 接口的方法,我们就得进行类型转换 C c = (C) b;但是 B,C 之间又没有明确的继承关系,所以Java 为了避免这种情况,就允许强制类型转换不正确的类型.
只有当你具体运行到代码的时候,才会根据实际类型抛出异常
#8
虽然说楼主的回答并没能解答我上面问题的疑惑,但楼主提到的这种运行处类型转换错误的原因真心受教了,的确这是个值得注意的点。
因为 Java 在编译的时候,根本就不会在意你的具体类型,只要你进行一次强制类型转换, Java 就默认你已经知道他的正确类型了
比如说,有一个类 A, 继承了 B,C 两种接口,然后我们令 B b = new A();那么如果希望使用到 b 的 C 接口的方法,我们就得进行类型转换 C c = (C) b;但是 B,C 之间又没有明确的继承关系,所以Java 为了避免这种情况,就允许强制类型转换不正确的类型.
只有当你具体运行到代码的时候,才会根据实际类型抛出异常
你确定范型擦除了数据了吗?擦除数据是当数组形式为 LIst<T>[]时才会将类型擦除,你直接使用 String[]类型是保留的,怎么会擦除类型呢
#9
虽然说楼主的回答并没能解答我上面问题的疑惑,但楼主提到的这种运行处类型转换错误的原因真心受教了,的确这是个值得注意的点。
因为 Java 在编译的时候,根本就不会在意你的具体类型,只要你进行一次强制类型转换, Java 就默认你已经知道他的正确类型了
比如说,有一个类 A, 继承了 B,C 两种接口,然后我们令 B b = new A();那么如果希望使用到 b 的 C 接口的方法,我们就得进行类型转换 C c = (C) b;但是 B,C 之间又没有明确的继承关系,所以Java 为了避免这种情况,就允许强制类型转换不正确的类型.
只有当你具体运行到代码的时候,才会根据实际类型抛出异常
你确定范型擦除了数据了吗?擦除数据是当数组形式为 LIst<T>[]时才会将类型擦除,你直接使用 String[]类型是保留的,怎么会擦除类型呢
#10
你的类型转换不是通过擦除得到的,范型都会有类似的类型转换,但是擦除和这种类型转换是不一样的,
当你使用范型的时候, java 会记录你的数据类型,当操作完成,再强制转换为之前应该有得类型,但这个不叫做擦除
当你使用范型的时候, java 会记录你的数据类型,当操作完成,再强制转换为之前应该有得类型,但这个不叫做擦除
#11
你的类型转换不是通过擦除得到的,范型都会有类似的类型转换,但是擦除和这种类型转换是不一样的,
当你使用范型的时候, java 会记录你的数据类型,当操作完成,再强制转换为之前应该有得类型,但这个不叫做擦除
没错,擦除后返回的是Object[],然后java记录下我的数据类型是String[],然后将Object[]转成String[],这就是我疑问的地方,因为和我问题开始提到的这种类型是会在运行时失败矛盾了。
难道java自动转换就可以成功,而我们认为转换就会失败?
#12
不一样的,从 String[]到 Object[]你可以认为是向上转型,虽然不能这么说,但可以这么想,然后, Object[]向 String[]转型是向下转型,可能会失败的.
而你第一个例子中, o 是一个 Object[]类型的数组,你将它强制向下转型为 String[],但是他的真实类型并不是 String[],所以运行时错误
而对于第二个例子,他的真实类型是 String[],先向上转型成功,然后再向下转型,真实类型是对的,也成功
而你第一个例子中, o 是一个 Object[]类型的数组,你将它强制向下转型为 String[],但是他的真实类型并不是 String[],所以运行时错误
而对于第二个例子,他的真实类型是 String[],先向上转型成功,然后再向下转型,真实类型是对的,也成功
#13
其他的我不太了解,我也是只知道,在java中子类可以强转成父类,但是反过来是有问题的
Public class A extends B{
}
A a = new A();
B b = new B();
B b = a;
Public class A extends B{
}
A a = new A();
B b = new B();
B b = a;
#14
自己顶起来。有高人意见不?现在只能模模糊糊的认为编译器会记住擦除前的具体类型,然后擦除后返回的Object或Object[]类型再由编译器自动转成之前记住的具体类型。。这是书上通俗的说法,但具体是怎么实现的?看反编译的文件也看不出来呀。
#15
擦除确实是擦除了,应该是编译的时候是擦除,你所使用的泛型成了object,但是在你运行的时候传入的是String,所有返回也是String也很正常
楼主比较纠结的问题是你看了编译文件,不正事编译时,擦除了吗
楼主比较纠结的问题是你看了编译文件,不正事编译时,擦除了吗
#16
擦除确实是擦除了,应该是编译的时候是擦除,你所使用的泛型成了object,但是在你运行的时候传入的是String,所有返回也是String也很正常
楼主比较纠结的问题是你看了编译文件,不正事编译时,擦除了吗

特地翻阅了英文版的《core java》,按书上的意思是,编译器会插入一个cast,来帮助我们强转,也就是说,字节码文件里面会根据你传入的参数类型生成强转的指令,而不是运行时强转的,运行时仅仅是checkcast一次,判断String[](引用的声明类型)和String[](编译器帮忙强转出来的类型)是否可以转换。
String[] s1 = getElement(i);
声明的类型是String[],即引用s1的类型
运行时右边方法传过去的类型也是String[],因为编译器帮忙强转了
可是,现在纠结的是,我反编译的代码里面看不到编译器插入的cast指令呀!所以不能验证书上的说法。所以头疼这个问题,现在不管了,就相信书上书法吧。
#17
我觉得是分配内存时的问题
也就是测试的问题 你那个方法是不同的
一个是
另一个是
也就是测试的问题 你那个方法是不同的
一个是
Object[] xxx = new Object[] {'"xxx", "yyy"};这个转换成String[] 就会失败 因为分配内存是按Object分的
另一个是
Object[] xxx = new String[] {"xxx", "yyy"};这个就是对的 因为分配内存时布局就是String
#18
第一个声明是Object[] ,然后向下转型为String[],而实际上是Object[] 所以报错。虽然java的数组是协变的,但是编译器不会推断Object数组内部的数据类型。
第二个声明为String[] ,对编译时期来说,编译器是知道你的类型是String[] 的,泛型的擦除跟楼主的问题没有根本上的联系,不信你可以直接声明数据类型自己强制转换。
在运行期,Object[] 引用的数据本身就是String[],所以是正确的强制转换。
第二个声明为String[] ,对编译时期来说,编译器是知道你的类型是String[] 的,泛型的擦除跟楼主的问题没有根本上的联系,不信你可以直接声明数据类型自己强制转换。
在运行期,Object[] 引用的数据本身就是String[],所以是正确的强制转换。