Reputation: 720
I ran into some code today that I found questionable. Here's a simplified example (not realistic).
public interface IListable {
//returns first n items from list
public ArrayList getFirstNThings(int n);
//returns last n items from list
public ArrayList getLastNThings(int n);
}
Then there's an implementor like so:
public GroceryList implements IListable {
private ArrayList<GroceryItem> groceries;
public GroceryList() {
this.groceries = new ArrayList<GroceryItem>();
}
public ArrayList<GroceryItem> getFirstNThings(int n) {
ArrayList<GroceryItem> firstNThings = new ArrayList<GroceryItem>();
for (int i=0; i < n; i++) {
firstNThings.add(this.groceries.get(i));
}
return firstNThings
}
public ArrayList<GroceryItem> getLastNThings(int n) {
ArrayList<GroceryItem> lastNThings = new ArrayList<GroceryItem>();
for (int i=this.groceries.size(); i < this.groceries.size()-n; i--) {
lastNThings.add(this.groceries.get(i-1);
}
return lastNThings;
}
}
Ignore any implementation problems you may find in that (I found a few too). What I'm getting at is that the interface does not use any generic type parameter for ArrayList (i.e ArrayList<?>), but the implementor of the interface's method does (i.e. ArrayList<GroceryList>). Other implementors may return ArrayLists with any other type parameters, no?
So my questions: Is this a problem? Should I refactor anything? Is it worth it? What's the advantage? What kind of problems can I run into if I have a method defined in an interface whose return type is a raw type, but the actual implementors of the method return various parameterized types?
Upvotes: 5
Views: 3683
Reputation: 7048
Best practice is to never return a List<?>
with pure wildcard in your Open code, just like you should not return null
.
Wildcard generics in Java will bring contamination to all coders using your code. You can do it in your Close code to resolve local problem.
Usually, you will avoid to return covariance and contravariance wildcards like List<? extends User>
and List<? super User>
You can do it if you know what you are doing and read everything about PECS, and bounded wildcards. Don't do it only because it compiles.
Upvotes: 0
Reputation: 36105
if both methods of IListable always return the same type, use this instead:
public interface IListable<T> {
//returns first n items from list
public ArrayList<T> getFirstNThings(int n);
//returns last n items from list
public ArrayList<T> getLastNThings(int n);
}
if this isn't an option, try using ? instead. While it's basically the same, it avoids ugly warnings.
public interface IListable {
//returns first n items from list
public ArrayList<?> getFirstNThings(int n);
//returns last n items from list
public ArrayList<?> getLastNThings(int n);
}
Generally, it's not a problem to use a more specific return type in an implementation than in a super-type or interface. If you're dealing with IListable, you need to handle any object type in the returned list. If you're dealing with GroceryList, you expect only GroceryItems. That's not only true for genric type arguments of return types, but for the return type itself as well. So if an interface specifies List<Foo> get()
, is okay to implement it as ArrayList<Foo> get()
.
Upvotes: 5