简体   繁体   中英

Why does Java generics doesnot allow returning its own type if the type is extended from anotehr class?

I have the DBMapper class that extends RowMapper. I parameterized it since two different mappings need to be done for two different different data sets. here is the code:

public class DBMapper<T> extends RowMapper<T>{
 
 T t;

 @Override
 public T mapRow (ResultSet rs, int rowNum){
   return mapData(t, rs, rowNum);
 }

 private <T extends B> T mapData(T data, ResultSet rs, int rowNum){
   //TODO mapping operations for Class B
    return data;
 }

 private <T extends C> T mapData(T data, ResultSet rs, int rowNum){
  //TODO mapping operations for class C
   return data;
 }

Here I am getting an error in the return statement of mapRow method. The error is

The method mapData(T extends B, ResultSet, int) in the type DBMapper<T> is not applicable for the arguments (T, ResultSet, int)

It is returning the type T which is valid. Even if T is extending another class, still it is type T that is returned. Why am I getting the above error?

Your type parameters T in the methods mapData() are hiding your original type Parameter. You should get a warning "The type parameter T is hiding the type T". As @Slaw pointed out in a comment, this method type parameter is an entirely new parameter, independent from the original T, just that you gave it the same name and thus hiding the other.

Furthermore the type parameters of Java Generics are not present anymore at runtime (keyword 'type erasure'). Therefore, even when you get rid of this error-message, this kind of case distinction between the types of T at runtime is unfortunately not possible using only the Java-Generics-syntax.

So either, as @Sweeper suggested, you have to make two different implementations in different classes, or you take the approach by @beneboo22 which you apparently prefer and do the instanceof-checking in only one single mapData() method with return type T (no additional type parameter):

private T mapData(T data, ResultSet rs, int rowNum){
     if (data instanceof B) {
         return data; //TODO implement map
     }
     if (data instanceof C) {
         return data; //TODO implement map
     }

     return data; // TODO implement default map
 }

@Sweeper 's solution instead would look like:

public abstract class DBMapper<T> extends RowMapper<T>{
 T t;

 @Override
 public T mapRow (ResultSet rs, int rowNum){
   return mapData(t, rs, rowNum);
 }

 abstract T mapData(T data, ResultSet rs, int rowNum);
}

class BMapper extends DBMapper<B>{
     @Override
     B mapData(B data, ResultSet rs, int rowNum){
         return data; //TODO implement map
     }
}
class CMapper extends DBMapper<C>{
     @Override
     C mapData(C data, ResultSet rs, int rowNum){
         return data; //TODO implement map
     }
}

( Edit: @dmitryvim 's answer seems equivalent to the instanceof approach to me. It would be different, if one could use T.class instead of t.getClass() , but because of type erasure, the former is not known at runtime anymore.)

public class DBMapper<T> extends RowMapper<T> {  


 T t;

 @Override
 public T mapRow (ResultSet rs, int rowNum){
   if (B.class.isAssignableFrom(t.getClass())) {
     return mapDataB(t, rs, rowNum);
   }
   if (C.class.isAssignableFrom(t.getClass())) {
     return mapDataC(t, rs, rowNum);
   }
   throw new IllegalArgumentException();
 }

 private <T extends B> T mapDataB(T data, ResultSet rs, int rowNum){
   //TODO mapping operations for Class B
    return data;
 }

 private <T extends C> T mapDataC(T data, ResultSet rs, int rowNum){
  //TODO mapping operations for class C
   return data;
 }

}
 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM