11

I have some interfaces that I intend third-parties to implement in the future, and I provide a base implementation myself. I'll only be using a couple to show the example.

Currently, they are defined as

Item:

public interface Item {

    String getId();

    String getName();
}

ItemStack:

public interface ItemStackFactory {

    ItemStack createItemStack(Item item, int quantity);
}

ItemStackContainer:

public interface ItemStackContainer {

    default void add(ItemStack stack) {
        add(stack, 1);
    }

    void add(ItemStack stack, int quantity);
}

Now, Item and ItemStackFactory I can absolutely foresee some third-party needing to extend it in the future. ItemStackContainer could also be extended in the future, but not in ways that I can foresee, outside my provided default implementation.

Now, I'm trying to make this library as robust as possible; this is still in the early-stages (pre-pre-alpha) so this may be an act of over engineering (YAGNI). Is this an appropriate place to use generics?

public interface ItemStack<T extends Item> {

    T getItem();

    int getQuantity();
}

And

public interface ItemStackFactory<T extends ItemStack<I extends Item>> {

    T createItemStack(I item, int quantity);
}

I fear that this may end up making implementations and usage more difficult to read and understand; I think it's the recommendation to avoid nesting generics wherever possible.

  • 1
    Kinda feels like you're exposing implementation details either way. Why does the caller need to work with and know/care about stacks rather than simple quantities? – cHao Sep 1 '15 at 4:51
  • That's something that I hadn't thought of. That does provide some nice insight into this specific problem. I'll make sure to make the changes to my code. – Zymus Sep 1 '15 at 4:56
9

You use generics in your interface when your implementation is likely to be generic as well.

For example, any data structure that can accept arbitrary objects is a good candidate for a generic interface. Examples: List<T> and Dictionary<K,V>.

Any situation where you want to improve type safety in a generalized way is a good candidate for generics. A List<String> is a list that only operates on strings.

Any situation where you want to apply SRP in a generalized way and avoid type-specific methods is a good candidate for generics. For example, a PersonDocument, PlaceDocument and ThingDocument can be replaced with a Document<T>.

The Abstract Factory pattern is a good use case for a generic, while an ordinary Factory Method would simply create objects that inherit from a common, concrete interface.

  • I don't really agree with the idea that generic interfaces should be paired with generic implementations. Declaring a generic type parameter in an interface and then refining or instantiating it in various implementations is a powerful technique. It's especially common in Scala. Interfaces abstract things; classes specialise them. – Benjamin Hodgson Sep 2 '15 at 22:28
  • @BenjaminHodgson: that just means you're producing implementations on a generic interface for specific types. The overall approach is still generic, though somewhat less so. – Robert Harvey Sep 2 '15 at 22:31
  • I agree. Perhaps I misunderstood your answer - you seemed to be advising against that sort of design. – Benjamin Hodgson Sep 2 '15 at 22:32
  • @BenjaminHodgson: Wouldn't a Factory Method be written against a concrete interface, rather than a generic one? (though an Abstract Factory would be generic) – Robert Harvey Sep 2 '15 at 22:34
  • I think we're agreeing :) I'd probably write the OP's example as something like class StackFactory { Stack<T> Create<T>(T item, int count); }. I can write up an example of what I meant by "refining a type parameter in a subclass" in an answer if you want more clarification, though the answer would only be tangentially related to the original question. – Benjamin Hodgson Sep 2 '15 at 22:39
8

Your plan of how to introduce generality to your interface appears to be correct to me. However, your question of whether that would be a good idea or not requires a somewhat more complicated answer.

Over the years I have interviewed a number of candidates for Software Engineering positions on behalf of companies that I have worked for, and I have come to realize that the majority of job seekers out there are comfortable with using generic classes, (for example, the standard collection classes,) but not with writing generic classes. Most have never written a single generic class in their career, and look as if they would be completely lost if asked to write one. So, if you force the use of generics upon anyone wishing to implement your interface or extend your base implementations, you may be putting people off.

What you can try, however, is to provide both generic and non-generic versions of your interfaces and your base implementations, so that people who do not do generics can live happily in their narrow, type-cast-heavy world, while people who do generics can reap the benefits of their broader understanding of the language.

3

Now, Item and ItemStackFactory I can absolutely foresee some third-party needing to extend it in the future.

There's your decision made for you. If you do not provide generics they will need to up-cast to their implementation of Item every time they use:

class MyItem implements Item {
}
MyItem item = new MyItem();
ItemStack is = createItemStack(item, 10);
// They would have to cast here.
MyItem theItem = (MyItem)is.getItem();

You should at least make ItemStack generic in the way you suggest. The deepest benefit of generics is that you almost never need to cast anything, and offering code that forces users to cast is IMHO a criminal offence.

2

Wow... I have a completely different take on this.

I can absolutely foresee some third-party needing

This is a mistake. You should not be writing code "for the future".

Also, interfaces are written top-down. You don't look at a class and say "Hey, this class could totally implement an interface and then I can use that interface somewhere else too". That is a wrong approach, because only the class which uses an interface truly knows what the interface should be. Instead you should be thinking "In this spot my class needs a dependency. Let me define an interface for that dependency. When I'm done writing this class, I will write an implementation of that dependency." Whether someone will ever re-use your interface and substitute your default implementation with their own is completely irrelevant. They may never do. This does not mean the interface was useless. It makes your code follow Inversion of Control principle, which makes your code very extensible in many places "just in case". There is no need to foresee anything. If you write it like that, you will always write quality code.

0

I think that using generics in your situation is too complex.

If you choice generics version of interfaces, the design indicates that

  1. ItemStackContainer< A > contains a single type of stack, A. (i.e. ItemStackContainer can't contain multiple types of stack.)
  2. ItemStack is dedicated to a specific type of item. So there is no abstraction type of all kind of stacks.
  3. You need to define a specific ItemStackFactory for each type of items. It's considered too fine as Factory Pattern.
  4. Write more difficult code such as ItemStack< ? extends Item >, decreasing maintainability.

These implicit assumptions and restrictions are very important things to decide carefully. So especially in early-stage, these premature designs should not be introduced. Simple, easy to understand, common design is desired.

0

I'd say it depends mainly on this question: If someone needs to extend the functionality of Item and ItemStackFactory,

  • will they just overwrite methods and so instances of their subclases will be used just like the base classes, just behave differently?
  • or will they add members and use the subclasses differently?

In the first case, there is no need for generics, in the second, there probably is.

0

Maybe, you can have a interface hierarchy, where Foo is one interface, Bar is another. Whenever a type must be both, it is a FooAndBar.

To avoid over desgin, only make the FooAndBar type when it is necessary, and keep a list of interfaces that will never extend anything.

This is a lot more comfortable to most programmers (most like their type checking or like to never deal with static typing of any kind). Very few people have written their own generics class.

Also as note: interfaces are about what possible actions can be run. They should actually be verbs not nouns.

  • This might be an alternative to using generics, but the original question asked about when to use generics over what you suggested. Can you improve your answer to suggest that generics are not necessary ever, but using multiple interfaces is the answer to all? – Jay Elston May 12 '16 at 7:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.