Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type safety: Unchecked cast from Object to List

Tags:

java

generics

SqlOutParameter out = new SqlOutParameter("out_refcursor",OracleTypes.CURSOR
                                           ,new StudentRowMapper());

// some code..

MapSqlParameterSource parameters = createMapSqlParameterSource();
parameters.addValue("in_studentid","101"); 

Map<String, Object> result = simpleJdbcCall.execute(parameters);
List<Student> students = (List<Student>) result.get("out_refcursor"); // here I get a warning

Definition of execute() method:

Map<String, Object> org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SqlParameterSource parameterSource)

"Execute the stored procedure and return a map of output params, keyed by name as in parameter declarations.."

The warning on the above line : List<Student> students = (List<Student>) result.get("out_refcursor"); is:

Type safety: Unchecked cast from Object to List

I understand it is only a compile time warning, and Off course I can do a @SuppressWarnings("unchecked") to suppress it.

Question: But how do I properly cast it?

  1. One way I tried out is

    List<Student> students = castObject( result.get("out_refcursor"));
    
    @SuppressWarnings("unchecked")
        private static <T extends List<?>> T castObject(Object obj){
        return (T)obj;
    }
    

Still I had to put @SuppressWarnings("unchecked") in the castObject() method. And I don't know if this is correct way of doing.

  1. Second way I tried is,

    List<?> students = castObject( result.get("out_refcursor"));
    
    Student student = (Student)students.get(0);
    
    private static <T extends List<?>> List<?> castObject(Object obj){
          if(obj instanceof List<?>) {
               return (List<Student>)obj;
          }
          return null;
     }
    

Any suggestions/advice are welcome.

like image 398
spiderman Avatar asked Sep 21 '25 11:09

spiderman


2 Answers

Some thing like that could work, as long as you know the class name of the element of the List. One issue here, the hard coded ArrayList instantiation.

public static <T> List<T> castList(Object obj, Class<T> clazz)
{
    List<T> result = new ArrayList<T>();
    if(obj instanceof List<?>)
    {
        for (Object o : (List<?>) obj)
        {
            result.add(clazz.cast(o));
        }
        return result;
    }
    return null;
}

Usage :

List<Student> students = castList(result.get("out_refcursor"), Student.class);
like image 199
ToYonos Avatar answered Sep 23 '25 04:09

ToYonos


Your second solution won't help (due to Type Erasure), and will cause other problems too so probably not a good idea.

The first method will probably work in practice, but is a bit dodgy really. There's a subtle (but huge) difference between sub-classing Generic types and subclassing their parameters in Java - i.e. ArrayList<Integer> is not a subclass of ArrayList<Object>, but ArrayList<String> is a sub-type of Collection<String>: see a more fun example. More formal Computer Sciencey background in Wikipedia entry on Covariance.

So, IMO you should keep the casting there, keep the (necessary) annotation, and catch the ClassCastException to make sure that the exceptional (hence the name) cases are covered.

like image 43
declension Avatar answered Sep 23 '25 05:09

declension