1

I ran into a problem when trying to specialize a class that implements a generic interface where I wanted to inherit from the same interface as the super class, but with a more specific type argument. The following snippet shows a synthetic but complete example that cannot be compiled. The comment contains the error message from the Java compiler.

interface Producer<T> {
    T get();
}

class NumberProducer implements Producer<Number> {
    @Override
    public Number get() { return null; }
}

// Producer cannot be inherited with different arguments: <java.lang.Integer> and <java.lang.Number>
class IntegerProducer extends NumberProducer implements Producer<Integer> {
    @Override
    public Integer get() { return null; }
}

In the PECS sense, Producer<T> is a producer, so Producer<Integer> would be a subtype of Producer<Number>, but there's no way to declare that in the definition of Producer<T>. Java does not allow IntegerProducer to inherit from NumberProducer and Producer<Integer> at the same time as IntegerProducer would then inherit from Producer<Integer> and Producer<Number> at the same time.

Is there a standard approach to this limitation, e.g. a pattern that solves the same problem without requiring this kind of inheritance?

Community
  • 1
  • 1
Feuermurmel
  • 9,490
  • 10
  • 60
  • 90
  • Why would you want to implement the interface in IntegerProducer? You can override the get() method without implementing the interface as well. – Rahul Bobhate May 07 '13 at 11:17
  • Could you provide an example of how these classes are meant to be used? I'm not sure even the common suggestion of making `NumberProducer` generic will do what you want this to do. If you use `NumberProducer implements Producer`, you can't return anything except `null` from `NumberProducer.get()` because you don't know what subtype of `Number` is `T`. That is, you can't return `new Number()` (I know that constructor doesn't exist, but bear with me) because `T` might, in fact, be `Integer`. – millimoose May 07 '13 at 11:37

3 Answers3

2

Just add a parameter to the super class:

interface Producer<T> {
    T get();
}

class NumberProducer<T extends Number> implements Producer<T> {
    @Override
    public T get() { return null; }
}

class IntegerProducer extends NumberProducer<Integer> { // Implicit: implements Producer<Integer>
    @Override
    public Integer get() { return null; }
}
WilQu
  • 7,131
  • 6
  • 30
  • 38
  • That's pretty much what I did now, thanks. I introduced a `SpecializableNumberProducer` and made `NumberProducer` inherit from it without adding any implementation but passing `Number` for the type parameter. This way, users of NumberProducer do not have to explicitly pass `Number` as type parameter on each usage. Ben Schulz's answer has more details on the implementation of generics and what prevents my original code from working. – Feuermurmel May 07 '13 at 13:48
1

Say we had a simple Method gimme.

public static <T> T gimme(Producer<T> p) { return p.get(); }

Within the context of gimme nothing is known about T. It could be Number, Integer or any other reference type. So, due to erasure, the compiler emits an interface call to Producer.get()Object rather than the specific call to, say, IntegerProducer.get()Integer. All types that implement Producer<T> with T != Object also implicitly implement Producer.get()Object. This implicit implementation forwards to the specific implementation. That might be NumberProducer.get()Number or IntegerProducer.get()Integer, but not both. That's why you can't implement the same interface twice.

Other languages allow this via definition site variance, where Producer<Integer> is a subtype of Producer<Number>, but alas, Java does not. The common workaround is to make NumberProducer generic as well.

Ben Schulz
  • 6,101
  • 1
  • 19
  • 15
  • I do understand that the call `p.get()` in your snippet will be compiled to a call to `Producer.get()Object` which will be implemented by `NumberProducer` but `IntegerProducer` could just override that method and have it call `NumberProducer.get()Integer` so I don't see a problem there. – Feuermurmel May 07 '13 at 13:34
  • @Feuermurmel In languages with declaration site variance that is possible; `IntegerProducer.get()` would override `NumberProducer.get()`. In Java, however, they would be overloaded methods and the compiler could only forward to one of them. Thus it's disallowed. – Ben Schulz May 07 '13 at 17:22
0

If you want to ensure that T is of a specific subtype you can use

interface Producer<T extends Number> 

Not sure what Producer reall is so I have to guess.

Update: If I understand you correct then I would say, you need to declare an interface which is a Producer. That's simple.

From this interface i would derive a new interface with the respecitve base type.

i.e.:

 interface Producer
 {
     base functions
 };


 interface Newproducer<T extends Producer>
 {
 };

Is this what you had in mind?

Devolus
  • 21,661
  • 13
  • 66
  • 113
  • The problem is that I want to use the `Producer` interface for types incompatible with `Number`, e.g. `String`. So I can't put a bound on the type parameter there. – Feuermurmel May 07 '13 at 11:41
  • Updated my approach. Not sure if this is what you had in mind. It also sounds a bit like a factory to me, but without knowing what you intend it's hard to say. – Devolus May 07 '13 at 11:50