Reputation: 3358
I'm trying to generify some calls to DB using MyBatis, but I've hit a wall so far.
Let's say I have this entities and mappers:
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
}
I'm getting this compilation error even though ClientMapper
is of type Insertable<Client>
, being Client
a type of Entity
.
The objective is getting to the following type-safe code:
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);
}
}
}
This way, I can just call the method insert
with an instance of any Entity
and then ask him to give me his mapper
so that I can insert it.
Is this possible? Am I doing something wrong?
Upvotes: 2
Views: 723
Reputation: 2969
It should compile with these changes:
public interface Entity {
<T extends Entity> Class<? extends Insertable<T>> mapper();
}
// ...
public <T extends Entity> Class<? extends Insertable<T>> mapper() {
return ClientMapper.class;
}
You cannot assign Class<ClientMapper>
to Class<Insertable<T>>
, for the same reason you cannot assign List<Integer>
to List<Number>
.
If you could, it would be unsafe:
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!
EDIT: This is not the only issue: the method returns something which depends on T
, but you return another one. The problem can be simplified:
class A {}
class B extends A {}
class C extends A {}
<T extends A> T getValue() {
return new C();
}
Here, T
might be resolved to any subtype of A
, so we cannot return an instance of C
because T
might be resolved to B
(e.g. by calling this.<B>getValue()
), and C
is not a subtype of B
.
Upvotes: 4