Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Java generics in interfaces that return collections. Best practice? Pitfalls?

Tags:

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?

like image 664
Troy Avatar asked Feb 10 '10 17:02

Troy


People also ask

What is the benefit of generics in collections framework in Java?

By using generics, you can develop generic or universal classes and methods that can accept any type as a parameter, thereby promoting reusability, efficiency, and maintainability of code. Hang on. Let's take a quick tour of the collections framework in Java before we delve deep into what generics has in store for us.

Why primitive data types are not allowed in Java generics?

So, anything that is used as generics has to be convertable to Object (in this example get(0) returns an Object ), and the primitive types aren't. So they can't be used in generics.

What are the advantages of using generics in regular code practice?

Code that uses generics has many benefits over non-generic code: Stronger type checks at compile time. A Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find.


1 Answers

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().

like image 199
sfussenegger Avatar answered Nov 15 '22 04:11

sfussenegger