Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interfaces and generics in Java

I have the code:

Set<? extends Notifiable> notifiables;

Notifiable is an interface. I don't understand the difference between the above code and:

Set<Notifiable> notifiables;

If Notifiable was a class then I'd understand the difference, the first code would allow Notifiable and any subclass of Notifiable whereas the second code would only allow Notifiable (and not any subclasses)

As you can't have an instance of an interface, what can I add/etc to the set? It seems to me there are only two options, either anything that implements Notifiable (in which case how is that different to the first code), or only "instances of Notifiable" which can't exist and so therefore nothing (which is pointless and should throw a compile time error).

like image 784
Jonathan. Avatar asked Mar 23 '14 18:03

Jonathan.


People also ask

Can we use generics with interface in Java?

Java Generic Interface In similar way, we can create generic interfaces in java. We can also have multiple type parameters as in Map interface. Again we can provide parameterized value to a parameterized type also, for example new HashMap<String, List<String>>(); is valid.

CAN interface have generics?

Generic interfaces can inherit from non-generic interfaces if the generic interface is covariant, which means it only uses its type parameter as a return value.

How is a generic interface defined?

A generic interface is primarily a normal interface like any other. It can be used to declare a variable but assigned the appropriate class. It can be returned from a method. It can be passed as argument.

Why generic is used in Java?

In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs.


2 Answers

A Set<Notifiable> can hold instances of classes that implement Notifiable. It's not limited to holding only instances whose concrete type is Notifiable (and you're right, there's no such thing). But a Set<Notifiable> guarantees that it can hold any kind of Notifiable, since it has an add(Notifiable) method that can accept anything that implements the interface.

Suppose that you have some classes called Foo and Bar which both implement Notifiable. If you create a Set<Foo> — that is, a set that's only allowed to contain instances of Foo and its subtypes — you can't pass it to a method that takes a Set<Notifiable>, because that method may add things that aren't Foo instances, such as Bar.

public void addABar(final Set<Notifiable> notifiables) {
    notifiables.add(new Bar());  // OK, since Bar is a subtype of Notifiable
}

public void wontWork() {
  final Set<Foo> foos = new HashSet<>();
  addABar(foos);  // Compile error, can't convert Set<Foo> to Set<Notifiable>
}

But sometimes you want to write a method that can accept things like Set<Foo> and Set<Bar> in addition to Set<Notifiable>. That's where the wildcard comes in. A Set<? extends Notifiable> guarantees that everything in it is some kind of Notifiable, but it doesn't guarantee that every kind of Notifiable can be added to it; it's allowed to be limited to a subtype. You can't call add() on it, because that method is now add(? extends Notifiable) instead of add(Notifiable) and you can't call a method whose argument type is unknown.

You'd typically use this when you don't need to add elements, but you do need to look at the existing elements and call Notifiable interface methods on them, and you want to allow the the caller to pass sets of subtypes such as Set<Foo>.

For example:

public void notifyAll(final Set<? extends Notifiable> notifiables) {
    for (final Notifiable notifiable : notifiables) {
        notifiable.notify();
    }
}

public void example() {
    final Set<Foo> foos = whatever();
    notifyAll(foos);  // OK, since a Set<Foo> is a Set<? extends Notifiable>
}

If notifyAll() took a Set<Notifiable>, you wouldn't be able to pass foos to it.

like image 113
Wyzard Avatar answered Oct 11 '22 22:10

Wyzard


Let's use a more straightforward example:

Set<? extends Serializable> serializables;

This one declares a variable which can hold a reference to a Set of Integers, Floats and so on:

 serializables = new HashSet<Serializable>(); // valid
 serializables = new HashSet<Number>(); // this is valid as well
 serializables = new HashSet<Integer>(); // valid

This one on the other hand:

Set<Serializable> serializables;

can only hold a Set of Serializable objects however:

serializables = new HashSet<Serializable>();
serializables = new TreeSet<Serializable>();

so this one will be a compiler error:

List<Serializable> numbers = new ArrayList<Integer>();

Corollary:

If you want a field which can hold any subtype of Notifiable then use:

Set<Notifiable> notifiables = new HashSet<Notifiable>();

if you want to restrict the subtype of Notifiable which can be used then this is the way to go:

Set<? extends Notifiable> notifiables = new HashSet<MyNotifiable>();

Addendum:

This is perfectly legal so you can retrofit your Set later as you see fit:

Set<? extends Notifiable> notifiables = new HashSet<NotifiableA>();
notifiables  = new HashSet<NotifiableB>(); 
like image 23
Adam Arold Avatar answered Oct 11 '22 22:10

Adam Arold