Reputation: 6542
The goal of our approach is to introduce interfaces to our existing DAO and model classes. The model classes are identified by resource-IDs of various types and the resource-IDs are not just random numbers, but carry semantics and behavior. Thus, we have to represent resource-IDs by objects rather than primitive types.
Current approach for resource-IDs:
interface ResourceId<T> {
T get();
}
class UserId implements ResourceId<String> {
public String get();
}
Current approach for our resources/models:
interface Resource<I extends ResourceId> {
I id();
}
class User implements Resource<UserId> {
public UserId id();
}
=== Option 1 ===
fails with:
error: > expected
multiple levels of generic types seem forbidden in Java
interface Dao<R extends Resource<I extends ResourceId>> {
R findById(I id);
void save(R u);
}
class UserDao implements Dao<User> {
public User findById(UserId id);
public void save(User u);
}
=== Option 2 ===
fails with:
UserDao is not abstract and does not override abstract method <R>save(R) in Dao
Also Dao<UserId> looks stupid. UserDao should be a Dao<User> object.
interface Dao<I extends ResourceId> {
<R extends Resource<I>> R findById(I id);
<R extends Resource<I>> void save(R u);
}
class UserDao implements Dao<UserId> {
public User findById(UserId id);
public void save(User u);
}
=== Option 3 ===
fails with:
UserDao is not abstract and does not override abstract method <I>findById(I) in Dao
Even if it worked, I is not bound by the ResourceId actually implemented by R.
interface Dao<R extends Resource> {
<I extends ResourceId> R findById(I id);
void save(R u);
}
class UserDao implements Dao<User> {
public User findById(UserId id);
public void save(User u);
}
=== Option 4 ===
compiles.
However #findById in UserDao would have to take a generic parameter of type ResourceId instead of UserId. Also, inside of the implementation of #findById we would have to cast the result of #get() to String.
Generally the problem is that the type of ResourceId is not bound by the ResourceId actually implemented by R.
interface Dao<R extends Resource> {
R findById(ResourceId id);
void save(R u);
}
class UserDao implements Dao<User> {
public User findById(ResourceId id);
public void save(User u);
}
=== Option 5 ===
compiles.
However Dao<User, UserId> looks stupid. The information which ResourceId we want to use (i.e. UserId) is already available in the implementation of the Resource (i.e. User). Is there no cleaner way?
interface Dao<R extends Resource, I extends ResourceId> {
R findById(I id);
void save(R u);
}
class UserDao implements Dao<User, UserId> {
public User findById(UserId id);
public void save(User u);
}
Any ideas how to properly solve this?
Upvotes: 1
Views: 297
Reputation: 2720
Your option # 1 will work just fine by tweaking it appropriately. First add the ResourceId
generic type specification to your Dao
declaration:
static interface Dao<I extends ResourceId<?>, R extends Resource<I>> {
R findById(I id);
void save(R u);
}
... and then do the same with the Dao
subclasses:
static class UserDao implements Dao<UserId, User> {
public User findById(UserId id) { return null; }
public void save(User u) {}
}
Since you don't need to know what is the generic type of a ResourceId
(<T>
) within the context of the Resource
class, you can just dismiss it there with a wildcard:
static interface Resource<I extends ResourceId<?>> {
I id();
}
Finally, the full code would compile as:
public class NestedGenerics {
static interface ResourceId<T> {
T get();
}
static class UserId implements ResourceId<String> {
public String get() { return null; }
}
static interface Resource<I extends ResourceId<?>> {
I id();
}
static class User implements Resource<UserId> {
public UserId id() { return null; }
}
static interface Dao<I extends ResourceId<?>, R extends Resource<I>> {
R findById(I id);
void save(R u);
}
static class UserDao implements Dao<UserId, User> {
public User findById(UserId id) { return null; }
public void save(User u) {}
}
}
Hope this helps.
Upvotes: 2