Java casting method without knowing what to do

Today I played with Java and I noticed something strange. Consider this code:

String foo = cast("hi"); int bar = cast("1"); 

The cast() method is here:

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

Java seems to distinguish "hi" from String, even if I did not miss a hint that it would be String , except for the type. It performs foo well, but does not work on bar , because you cannot use String for Integer. What's going on here?

I have two guesses:

  • The cast method returns Object , and upon initialization, it automatically switches to the type.

    This doesn't make sense as it would give me:

     Type mismatch: cannot convert from Object to int 
  • Java seems to know what it needs to pass to String or whatever the variable type is. But how does it work?

+5
java casting
source share
4 answers

Your translation method performs an unchecked conversion that is specifically used by the JVM to provide backward compatibility with non-generic code.

Such calls cannot be shown as statically safe in the generic type system. Rejecting such calls will invalidate the large bodies of the existing code and prevent them from using newer versions of the libraries. JLS 5.1.9

A method call without explicit type parameters will cause the compiler to output a parameter of type invocations, in this case, based on their expected return type. Output Type , JLS 12.15.2.7. . This means that the code is equivalent to this:

 String foo = Caster.<String>cast("hi"); // no exception int bar = Caster.<Integer>cast("1"); // runtime ClassCastException 

Primitive types will be displayed in their version box:

If A is a primitive type, then A is converted to a reference type U by means of a box conversion, and this algorithm is applied recursively to the constraint U <F. JLS 12.15.2.7.

The JVM provides type safety by performing runtime type checks for return values โ€‹โ€‹of functions containing uncontrolled casts, at the first point where the type information is not erased (I did not find it explicitly specified in the specification, but it looks like this: work this way, although this mentioned in Java Tutorials ). In this case, when you try to assign a value to a typed local variable, the type of the return value is checked, resulting in a ClassCastException .

To give more detailed information about when a forced execution check is performed at run time, consider a few more examples:

 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 

EDIT:

Here is the StackOverflow question about when runtime type checks are performed: When is the total return value of a function different after deleting styles?

+6
source share
 public static <T> T cast(Object value) { return (T) value; } 

The important part is the <T> method, which means that the method will return a type based on the expected method call.

 String foo = cast("hi"); // left side is a String, <T> will be typed as String 


The second is a bit more complicated since Generic types cannot be primitive types. I think it returns the most common type of 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 
+7
source share

I do not see any strange behavior.

IN:

 String foo = cast("hi"); 

"hi" is an Object (like everything else in Java), so it will be accepted by the compiler as an argument to Object . After the call, the cast() function returns "hi", which is a string, and it is assigned String foo . There is no strange behavior.

IN:

 int bar = cast("1"); 

"1" is an object, and it is accepted by the compiler. The cast function returns "1", which is a string. Unlike other languages โ€‹โ€‹(e.g. Javascript), a string cannot be directly written as a whole. There is no strange behavior.

If you change the cast() function to:

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

You will get an error at compile time instead of runtime.

0
source share

If someone needs to use primitive types as well, you can refer to this.

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

These tests pass, here, below, the related parts from my 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()); } 
0
source share

All Articles