我今天正在玩Java,我注意到一些奇怪的东西。请考虑以下代码:
String foo = cast("hi");
int bar = cast("1");
cast()
方法在这里:
public static <T> T cast(Object value) {
return (T) value;
}
Java似乎将"hi"
强制转换为String,即使我没有传递任何提示它是String
,但类型除外。它foo
确实很好,但在bar
上失败,因为你不能将String转换为整数。这里发生了什么?
我有两个猜测:
强制转换方法返回Object
,初始化时会自动转换为该类型。
这没有意义,因为它会给我:
Type mismatch: cannot convert from Object to int
Java似乎知道它需要转换为String,或者变量的类型。但是这如何运作?
答案 0 :(得分:7)
public static <T> T cast(Object value) {
return (T) value;
}
重要的部分是方法上的<T>
,这意味着该方法将根据方法调用的内容返回一个类型。
String foo = cast("hi"); // left side is a String, <T> will be typed as String
第二个是有点棘手,因为泛型类型不能是原始类型。我猜它会返回最常见的类型Object
。
int bar = cast("1"); // left side is primitive
// <T> will be typed by with the corresponding non-primitive type.
// But it fails because "1" cannot be casted to java.lang.Integer
答案 1 :(得分:6)
您的强制转换方法执行unchecked conversion,它在JVM中专门处理,以保持与非泛型代码的向后兼容性。
在使用泛型的类型系统下,此类调用无法显示为静态安全。拒绝此类调用将使大量现有代码无效,并阻止它们使用较新版本的库。 JLS 5.1.9
调用没有显式类型参数的方法将导致编译器推断调用的类型参数,在这种情况下基于它们的预期返回类型。 Type Inference,JLS 15.12.2.7.。这意味着代码与此等效:
String foo = Caster.<String>cast("hi"); // no exception
int bar = Caster.<Integer>cast("1"); // runtime ClassCastException
原始类型将推断为其盒装版本:
如果A是基本类型,则通过装箱转换将A转换为引用类型U,并且该算法递归地应用于约束U&lt;&lt; F. JLS 15.12.2.7.
JVM通过在未删除类型信息的第一点对包含未经检查的强制转换的函数的返回值进行运行时类型检查来确保类型安全(我没有在规范中明确说明它,但事情看起来如此以这种方式工作,虽然在The Java Tutorials中提到过。在这种情况下,如果您尝试将值分配给类型化局部变量,则会检查返回值的类型,从而生成ClassCastException
。
为了更好地了解强制执行运行时类型检查强制转换的时间,下面是一些示例:
Object a3 = Caster.<String>cast(3); // no exception, a3 is now Integer
Object a4 = (String)Caster.<String>cast(3); // an explicit cast causes runtime ClassCastException
修改强>
以下是关于何时强制执行运行时类型检查的StackOverflow问题:When is generic return value of function casted after type erasure?
答案 2 :(得分:0)
我没有看到任何奇怪的行为。
在:
String foo = cast("hi");
"hi"
是Object
(作为Java中的所有内容),因此它将被编译器接受为Object
参数。通话结束后,cast()
函数返回&#34; hi&#34;,这是一个字符串,并将其分配给String foo
。这里没有奇怪的行为。
在:
int bar = cast("1");
&#34; 1&#34;是一个对象,它被编译器接受。
cast
函数返回&#34; 1&#34;,这是一个字符串。与其他语言(例如Javascript)不同,字符串不能直接转换为整数。这里没有奇怪的行为。
如果您将cast()
功能更改为:
public static <T> T cast(T value) {
return value;
}
编译时会出错,而不是执行时间。
答案 3 :(得分:0)
如果任何人也需要转换基本类型,可以参考此。
public class ReflectionTests {
@Test
public void TEST_PRIMITIVE_TYPE_CAST() {
Object myNumber=123456;
int number=ReflectionUtils.cast(myNumber);
assertTrue(number==123456);
int numberTest2=ReflectionUtils.castPrimitiveType(myNumber, myNumber.getClass().getName());
assertTrue(numberTest2==123456);
double numberTest3=ReflectionUtils.castPrimitiveType(myNumber, "double");
assertTrue(numberTest3==123456);
}
}
这些测试通过了,在我的ReflectionUtils的相关部分下面
public static <T> T cast(Object value) {
if(value.getClass().isPrimitive())
return castPrimitiveType(value, value.getClass().getName());
return (T) value;
}
public static <T> T castPrimitiveType(Object value,String typeName) {
switch (typeName) {
case "double":
return (T) castToDouble(value);
case "byte":
return (T) castToByte(value);
case "int":
return (T) castToInt(value);
case "long":
return (T) castToLong(value);
case "float":
return (T) castToFloat(value);
default:
break;
}
return (T) value;
}
public static Boolean castToBoolean(Object val) {
return Boolean.valueOf(val.toString());
}
public static Byte castToByte(Object val) {
return Byte.valueOf(val.toString());
}
public static Character castToChar(Object val) {
return Character.valueOf(val.toString().charAt(0));
}
public static Integer castToInt(Object val) {
return Integer.valueOf(val.toString());
}
public static Long castToLong(Object val) {
return Long.valueOf(val.toString());
}
public static Float castToFloat(Object val) {
return Float.valueOf(val.toString());
}
public static Double castToDouble(Object val) {
return Double.valueOf(val.toString());
}