Oreste Viron Oreste Viron - 5 months ago 13
Java Question

Generics and cast

I try to write a class that take a parameter name and can return the corresponding parameter of a given object. Currently, my class look like this :

public class ParamValue<T> {

private String paramName;

@SuppressWarnings("unchecked")
public T getValue(Object obj) throws Exception {

Class<?> c = obj.getClass();
Field param = c.getDeclaredField(paramName);
boolean isAccessible = param.isAccessible();
param.setAccessible(true);
Object value = param.get(obj);
param.setAccessible(isAccessible);
return (T) value;
}

// get set ...
}


Now, imagine that we have an object with a simple Long parameter :

public class ExampleClass {

private Long value;

// get set ...
}


We can do this to get back the long value :

ExampleClass ec = new ExampleClass();
ec.setValue(12L);

ParamValue<Long> pvString = new ParamValue<>();
pvString.setParamName("value");

// print 12
System.out.println(pvString.getValue(ec));


Now, if I declare the "ParamValue" as a Point for example, it still works :

ExampleClass ec = new ExampleClass();
ec.setValue(12L);

ParamValue<Point> pvPoint = new ParamValue<>();
pvPoint.setParamName("value");

// print 12
System.out.println(pvPoint.getValue(ec));


But, as Point cannot be cast to Long, I expected some exception, like ClassCastException.

I know java compiler do some type erasure in compilation time, but I thought the compiler would automatically try to cast to Point, and fail, to the output of "pvPoint.getValue(ec)"

Can someone explain how this work ?

Thanks

Answer

ClassCastException is a RuntimeException, which means that it will be thrown only at runtime and not at compilation time. Since your value reference Object value = param.get(obj); is of type Object, your cast to type should be allowed since all classes extend Object. Your getValue method accepts any Object as a parameter and therefore at compilation time, any class will be accepted, regardless of the parametrized type you declared.

If you wanted type safety, you could declare the parameter of the method as <? extends T> and then only T and classes which extend it would be allowed for the method call.

You could also modify the method like this:

    public <T> T getValue(Class<T> returnType, Object obj) throws Exception {
        Class<?> c = obj.getClass();
        Field param = c.getDeclaredField(paramName);
        boolean isAccessible = param.isAccessible();
        param.setAccessible(true);
        Object value = param.get(obj);
        param.setAccessible(isAccessible);
        return returnType.cast(value);
}
Comments