Reputation: 15008
Consider the following classes
public interface SortBy<S> {
}
public class CommentSortBy<S> implements SortBy<S> {
public static CommentSortBy<Date> CREATION = new CommentSortBy<Date>();
public static CommentSortBy<Integer> VOTES = new CommentSortBy<Integer>();
}
public class SomeQueryUnsafe {
public <M, S extends SortBy<M>> void setSort(S sortBy, M min) {
//Set relevant values
}
}
This is currently used as:
public SomeQueryUnsafe createCommentQueryUnsafe() {
return new SomeQueryUnsafe();
}
public void test() {
createCommentQueryUnsafe().setSort(CommentSortBy.CREATION, new Date());
}
While this works, the problem is that createCommentQueryUnsafe()
does not specify limits on sortBy
. Users are free to pass UserSortBy.NAME
even though that would make no sense in this context
I can't figure out how to do write this though because just adding <B extends SortBy>
to the class signature means I loose the ability to restrict the min
parameter in the method. I can't use something like <M, S extends B & SortBy<M>>
as its a compiler error. Other attempts with wildcard magic just result in significantly more complexity and compiler errors. Moving the sorting to the createCommentQuery()
method would mean every single query needs 2 methods, which is a crazy amount of duplicated code
How can I possibly write the generics so createCommentQuery()
limits the sortBy
parameter to just CommentSortBy
while still having min
restricted to the S parameter in the SortBy class?
Upvotes: 4
Views: 526
Reputation: 55223
This is indeed a tricky issue for the reasons you've pointed out. I tried various approaches but they were all defeated by the generics limitation you cited. Ultimately it seems like you'll need to make some design changes if you want the specified type safety.
Using the inheritance hierarchy of the SortBy
implementations for your generic type restrictions seems to have led to this impasse in particular. I tried decoupling that restriction into a new type parameter on SortBy
, which stands for the queried object itself, e.g. Comment
, User
, etc. This is the design I came up with:
static class Comment { }
static class User { }
interface SortBy<T, M> { }
static class CommentSortBy<M> implements SortBy<Comment, M> {
static final CommentSortBy<Date> CREATION = new CommentSortBy<Date>();
static final CommentSortBy<Integer> VOTES = new CommentSortBy<Integer>();
}
static class UserSortBy<M> implements SortBy<User, M> {
static final UserSortBy<String> NAME = new UserSortBy<String>();
}
static class Query<T> {
public <M> void setSort(SortBy<T, M> sortBy, M min) {
//Set relevant values
}
}
public static void main(String[] args) {
new Query<Comment>().setSort(CommentSortBy.CREATION, new Date());
new Query<Comment>().setSort(UserSortBy.NAME, "Joe"); //compiler error
}
(ideone)
Upvotes: 3