我今天在玩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大小写为Integer。 这是怎么回事

我有两个猜测:

转换方法返回Object,并在初始化时自动转换为该类型。

这没有意义,因为它会给我:

Type mismatch: cannot convert from Object to int

Java似乎知道它需要强制转换为String或变量的类型。 但这如何工作?

@KLibby在方法中声明。 它说public static < T > T cast(Object value) {。 注意< T >。

类声明只是public class Test {。

第二个给了我一个运行时异常,而不是编译时错误。 那是你在说什么吗?

我使用eclipse,因此类型不匹配会对我"大喊"。

嗯,Eclipse和Java之间有区别。 Java不会在运行时对您大吼大叫。

@KLibby-这是一个通用方法; 它与泛型类无关。 即使该类是通用的,且类型参数名为T,该方法的类型参数也会遮盖该类参数。

重新发明轮子? String str = String.class.cast(obj);

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

我认为对于int,它实际上已转换为Integer。 那就是我得到的错误消息:java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

是的,空气中充满了神奇的魔力:)好的,原始元素似乎已被它们的非原始类所替代。 @markspace thansk,我更正了我的错误。

@AnthonyRaymond它称为自动拆箱

您的cast方法执行未经检查的转换,该转换在JVM中进行了特殊处理,以保持与非通用代码的向后兼容性。

Such calls cannot be shown to be statically safe under the type system using generics. Rejecting such calls would invalidate large bodies of existing code, and prevent them from using newer versions of the libraries. JLS 5.1.9

在没有显式类型参数的情况下调用该方法将导致编译器根据调用的预期返回类型来推断调用的类型参数。类型推断,JLS 15.12.2.7 ..这意味着代码与此等效:

String foo = Caster.cast("hi"); // no exception

int bar = Caster.cast("1");    // runtime ClassCastException

基本类型将推断为其盒装版本:

If A is a primitive type, then A is converted to a reference type U via boxing conversion and this algorithm is applied recursively to the constraint U << F. JLS 15.12.2.7.

JVM通过在不删除类型信息的第一点上对包含未经检查的强制类型转换的函数的返回值进行运行时类型检查来确保类型安全(我没有在规范中明确声明它,但是事情看起来是这样工作的) ,尽管在Java教程中有提到)。在这种情况下,当您尝试将值分配给键入的局部变量时,将检查返回值的类型,结果为ClassCastException。

为了在强制执行的运行时类型检查强制转换发生时提供更多的信息,这里有更多示例:

Object a3 = Caster.cast(3); // no exception, a3 is now Integer

Object a4 = (String)Caster.cast(3); // an explicit cast causes runtime ClassCastException

编辑:

这是一个关于何时何时执行运行时类型检查的StackOverflow问题:类型擦除后何时强制转换函数的通用返回值?

如果任何人也需要转换基本类型,可以参考此内容。

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());
}

我看不到任何奇怪的行为。

在:

String foo = cast("hi");

"hi"是Object(与Java中的所有内容一样),因此编译器会将其作为Object自变量接受。调用之后,cast()函数返回作为字符串的" hi",并将其分配给String foo。这里没有怪异的行为。

在:

int bar = cast("1");

" 1"是一个对象,被编译器接受。

cast函数返回" 1",它是一个字符串。与其他语言(例如Javascript)不同,字符串不能直接转换为整数。这里没有怪异的行为。

如果将cast()函数更改为:

public static < T > T cast(T value) {
return value;
}

您将在编译时而不是执行时得到一个错误。