2017-01-23 69 views
1

我试图使用MyBatis来生成一些对数据库的调用,但到目前为止我已经碰壁了。接口中的泛型类型和多态性

比方说,我有这样的实体和映射器:

public interface Entity { 

    <T extends Entity> Class<Insertable<T>> mapper(); 
} 

public interface Insertable<T extends Entity> { 

    void insert(T entity); 
} 

public interface ClientMapper extends Insertable<Client> { 

    void insert(Client client); 
} 

public interface CampaignMapper extends Insertable<Campaign> { 

    void insert(Campaign campaign); 
} 

public class Client implements Entity { 

    private final Long id; 

    public Client(final Long id) { 
     this.id = id; 
    } 

    @Override 
    public <T extends Entity> Class<Insertable<T>> mapper() { 
     return ClientMapper.class; // Incompatible types error 
    } 

我越来越即使ClientMapperInsertable<Client>式的,可Client一种类型的Entity这个编译错误。

目标越来越为以下类型安全代码:

public class MapperOperation { 

    public static void insert(Entity entity) { 
     insert(entity, entity.mapper()); 
    } 

    private static <V extends Entity, K extends Insertable<V>> void insert(V entity, Class<K> mapperClass) { 
     try (SqlSession session = PersistenceManager.get().openSession()) { 
      K mapper = session.getMapper(mapperClass); 
      mapper.insert(entity); 
      session.commit(); 
     } catch (Exception e) { 
      log.error("Could not insert entity {}", entity, e); 
     } 
    } 
} 

这样一来,我就可以调用该方法insert任何Entity的一个实例,然后请他给我他的mapper使我可以插入它。

这可能吗?难道我做错了什么?

+0

我有一个“偏离主题”的问题:接口'ClientMapper'和'CampaignMapper'做了什么?我的意思是他们只是重新声明插入方法,具有相同的签名... –

+0

这就是Mybatis的工作原理(据我所知)。每个接口都有一个与将要执行的查询关联的XML。然后使用会话获得该接口的实例(mapper),当您调用该方法时,Mybatis会发挥神奇功效。 – carcaret

回答

4

应这些变化编译:

public interface Entity { 
    <T extends Entity> Class<? extends Insertable<T>> mapper(); 
}  

// ... 

public <T extends Entity> Class<? extends Insertable<T>> mapper() { 
    return ClientMapper.class; 
} 

不能分配给Class<ClientMapper>Class<Insertable<T>>,你不能分配给List<Integer>List<Number>同样的原因。

如果你能,这将是不安全的:

List<Integer> li = new ArrayList<>(); 
List<Number> ln = li; // does not compile 
ln.add(3.14); 
Integer i = li.get(0); // it's a Double! 

编辑:这并不是唯一的问题:该方法返回的东西依赖于T,但是你回到一个又一个。这个问题可以简化为:

class A {} 
class B extends A {} 
class C extends A {} 

<T extends A> T getValue() { 
    return new C(); 
} 

这里,T可能被解析到的A任何亚型,所以我们不能返回C一个实例,因为T可能(通过调用this.<B>getValue() EG)解析为B,并C是不是B的子类型。

+0

它仍然给我同样的错误'不兼容的类型:java.lang.Class 不能转换为java.lang.Class <?延伸可插入>' – carcaret

+0

是的,这不是唯一的问题。我编辑了我的答案。 – rom1v

+0

我想我看到了问题。猜猜这里没有解决方法,对吧?最终,我想要完成的是调用'insert(entity,entity.mapper())'是类型安全的。 – carcaret