Generics and casting

I am trying to write a class that takes a parameter name and can return the corresponding parameter of this object. Currently, my class is as follows:

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 return a 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 "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, since Point cannot be attributed to Long, I expected some exception, for example ClassCastException.

I know that the java compiler does some type erasure during compilation, but I thought that the compiler will automatically try to pass to Point and play on the output "pvPoint.getValue (ec)"

Can someone explain how this works?

thank

+4
4

ClassCastException - RuntimeException, , , . Object value = param.get(obj); Object, cast , Object. getValue , .

, <? extends T>, T , .

:

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

. return - :

     if (returnType.isInstance(value)) {
        return returnType.cast(value);
    } else {
        // could be replaced with a return null
        throw new ClassCastException("Exception message");          
    }
+2

, , . .

, ClassCastException, , . @SuppressWarnings. :

class ParamValue<T> {

    private final Class<T> clazz;
    private final String paramName;

    ParamValue(Class<T> clazz, String paramName)
    {
        this.clazz = clazz;
        this.paramName = paramName;
    }

    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 clazz.cast(value);
    }
}

:

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

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

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

(T) . .

PrintStream.println , Object. , .

ClassCastException:

  • :

    final Point value = pvPoint.getValue(ec); // CCE
    System.out.println(value);
    
  • Provide an instance Class<T>for ParamValueruntime checks:

    public class ParamValue<T> {
        private final Class<T> clazz;
    
        public ParamValue(Class<T> clazz) {
            this.clazz = clazz;
        }
    
        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 clazz.cast(value); // instead of (T) value
        }
    
        // get set ...
    }
    

    And then:

    ParamValue<Point> pvPoint = new ParamValue<>(Point.class);
    pvPoint.setParamName("value");
    System.out.println(pvPoint.getValue(ec)); // CCE
    
+1
source

I found a bizarre thing if someone goes in there. If I extend the ParamValue class like thie:

public class ParamPoint extends ParamValue<Point> {
}

Erasing a type is only partially applied, no exception is thrown, but you can still check the type as follows:

ParamValue<Point> pvPoint = new ParamPoint();

ParameterizedType superclassType = (ParameterizedType) pvPoint.getClass().getGenericSuperclass();
Type paramType = superclassType.getActualTypeArguments()[0];

// print true
System.out.println(paramType.equals(Point.class));

Hope this helps someone.

0
source

All Articles