1
votes
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.

2
If you know that the returned value is a List<Student>, you're fine.Sotirios Delimanolis
The first way is fine if you know it will always return List<Student> if you aren't sure what it will return you should use your third approach...brso05
I know it will always return List<Student>, So it is clean coding to put @SupressWarnings("unchecked") in that case? The third approach will avoid the warning too, but I felt too much codespiderman

2 Answers

4
votes

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.

8
votes

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