4

I've got an interface defined like this:

public interface AdditionalProcessing<Pre, Post> {
    public void postProcess(Pre pre, Post post);
}

And I've decided I want to make a NoAdditionalProcessing class that extends it. It's a no-op that won't do anything for its implementation. Here's my implementation that compiles:

public class NoAdditionalProcessing implements AdditionalProcessing {

    @Override
    public void postProcess(Object o, Object o2) {

    }
}

The problem is, when I pass this into a method signature like this:

public <I, O> O map(I source, Class<O> destinationClass, AdditionalProcessing<I, O> additionalProcessing) throws MappingException 

It gives me a warning on the method call that say:

Unchecked assignment: ...NoAdditionalProcessing to ...AdditionalProcessing

Those ...'s are the package names which I've excluded. Is there a way to get rid of this warning? I basically want to define NoAdditionalProcessing so it says, "I'll accept anything, it's irrelevant and that's ok".


I've tried this:

public class NoAdditionalProcessing implements AdditionalProcessing<Object, Object>

But that gives me an error because it is requiring that I pass in Object. I've tried

public class NoAdditionalProcessing implements AdditionalProcessing<? extends Object, ? extends Object>

But that doesn't compile.

Roman C
  • 49,761
  • 33
  • 66
  • 176
Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • 2
    Why not `public class NoAdditionalProcessing
     implements AdditionalProcessing
    `? You can then create a `new NoAdditionalProcessing` if you want.
    – assylias Jun 17 '13 at 17:34
  • `public class NoAdditionalProcessing implements AdditionalProcessing extends Object, ? extends Object>` should be `public class NoAdditionalProcessing implements AdditionalProcessing, ?>`, because extending `Object` is implicit. – AJMansfield Jun 17 '13 at 17:36
  • 1
    @AJMansfield `The type NoAdditionalProcessing cannot extend or implement AdditionalProcessing,?>. A supertype may not specify any wildcard`. – Matt Ball Jun 17 '13 at 17:38
  • @MattBall I know that, its just coding style, to improve clarity. – AJMansfield Jun 17 '13 at 17:40
  • 1
    "But that gives me an error because it is requiring that I pass in Object" What error does it give you? That solution looks like it should almost certainly be the right one, to me. (You may need to widen the generics on the `map` function, though.) – Louis Wasserman Jun 17 '13 at 17:41
  • @assylias that works for me. Post that as an answer if you want. – Daniel Kaplan Jun 17 '13 at 17:42

5 Answers5

4

Change your class declaration to give values to the type parameters:

public class NoAdditionalProcessing 
    implements AdditionalProcessing<Object, Object>

Then, change your method signature to have a super wildcard so that you can pass in NoAdditionalProcessing for any destination class:

public <I, O> O map(I source, Class<O> destinationClass, 
    AdditionalProcessing<? super I, ? super O> additionalProcessing)
Russell Zahniser
  • 16,188
  • 39
  • 30
  • See my answer of why I believe it should actually be `? extends O`. – Has QUIT--Anony-Mousse Jun 17 '13 at 18:16
  • @Anony-Mousse: In that case an `AdditionalProcessing` would not be able to do post-processing for ``. – Russell Zahniser Jun 17 '13 at 18:20
  • Yes. This is intentional, because a `noop` processing does not *know* how to convert apples to oranges. In my answer I've now also covered this. All variants (`extends` vs. `super` in each parameter) *can* occur, and the OP needs to decide which one he actually has. – Has QUIT--Anony-Mousse Jun 17 '13 at 18:21
  • If it were a `Converter` that had `O convert(I in)`, that would make sense. But here the object is post-processing after the conversion. It doesn't have to know how to make the conversion. – Russell Zahniser Jun 17 '13 at 18:23
  • Well, the second type is a `destinationClass`... sounds like some kind of conversion to me. – Has QUIT--Anony-Mousse Jun 17 '13 at 18:26
  • Yes, and the `map()` method uses it as such - but then it applies a post-processing step. The noop postprocessor should be able to postprocess any mapping. If you use `extends` instead of `super`, then it can't be used except when `O` is `Object`. – Russell Zahniser Jun 17 '13 at 18:29
  • We don't know his `map` function. He may have oversimplified his code, and actually want a `convert`. – Has QUIT--Anony-Mousse Jun 17 '13 at 18:32
  • After reading http://stackoverflow.com/a/1911036/61624 I get this answer now. You've changed the method signature to say, "Pass in anything that's I/O or anything that's a parent of I/O. Since `Object` is a parent of everything, this will work. – Daniel Kaplan Jun 17 '13 at 19:23
3

This is the long version of why I consider @RussellZahniser solution superior to @assylias solution (but I also add in another twist):

Whenever you use generics, consider whether you want to allow subtypes or supertypes.

While it may seem like the compiler being overly pedantic at first, there is an odd inversion of compability in generics. Roughly said: A bag of apples is not a bag of fruits. In a bag of fruits, you may place bananas, but not in a bag of apples.

So whenever using generics, consider whether you want to allow subtypes (for consumption) or supertypes (for production).

Assuming that your AdditionalProcessing is supposed to be a converter, you may actually want to write it as AdditionalProcessing<IN, OUT>.

So when is an AdditionalProcessing compatible to another?

Assuming you need a converter of type AdditionalProcessing<Fruit, Fruit>. It must accept any kind of fruit, but it also does not matter which kind of fruit it returns. I.e. it may convert apples to oranges. However, it must not be a converter of apples to stones, because then you can't feed oranges, and you don't get fruit out.

So for the converter case, your map function should actually look like this:

public <I, O> O map(I source, Class<O> destinationClass, 
    AdditionalProcessing<? super I, ? extends O> additionalProcessing)

Since:

? super I: "accepts at least fruits (but maybe something else, too!)".

? extends O: "returns some kind of fruit (but maybe only apples)".

I found the fruit example to be really really valueable.

Your NoAdditionalProcessing would then look like this:

public class NoAdditionalProcessing<TYPE>
  implements AdditionalProcessing<TYPE, TYPE> {
    @Override
    public void postProcess(Object o, Object o2) {
         // Actually I would have expected something like "return o1;"
    }
}

Which is essentially a fusion of the two answers I credited in the first line. However, it is hard to tell from your question whether you want super or extends. Which is why I want to highlight that both may be reasonable, and you may want to have both.

Usually, it will be super for input types (i.e. consumed data), and extends for output types (i.e. produced types). (It gets more messy with nested generics.) I.e. for a ComapareTwoTypes<FIRST, SECOND>, you will want to allow a comparator that accepts super types for both.

Some examples as patterns:

  • Validator<? super I, ? super O>

    It is acceptable if it is performing validation on a super type only. It consumes both types, for comparison, and produces nothing.

  • Converter<? super I, ? extends O>

    It may chose to accept super types, and it may restrict its return values to some super type. It consumes the first type, produces the second.

It's much harder to come up with an example where both are extends, because the Java compiler won't be able to infer the type reliably without additional hints. This mostly happens when I and O themselves are generics, e.g. collections of another type, because of contravariance of collections.

Wikipedia has an article on covariance and contravariance of types that may help to shine some light on the situations arising here (unfortunately, the article is a bit theoretical). But collections are the most obvious case. I think it was some C++ that had this "a bag of apples is not a bag of fruit: you are not allowed to put a banana in it" example, although the primary one involves putting submarines in a car parking lot (if a parking-lot-of-cars were a kind-of parking-lot-of-vehicles; but they aren't).

Has QUIT--Anony-Mousse
  • 76,138
  • 12
  • 138
  • 194
  • Good answer. I still don't understand the difference of `super` vs `extends` though. I think this explains it better: http://stackoverflow.com/a/1911036/61624 – Daniel Kaplan Jun 17 '13 at 19:15
  • That one only tells you what `super` and `extends` *literally* mean; not gives you an actual guideline when to use which. I like the PECS answer to that question better, because that actually gives you a good default rule to choose `super` vs. `extends`. But again, things get really messy once you have constructs such as `applyFilter(Collection extends I> col, Filter super I> filter)`. – Has QUIT--Anony-Mousse Jun 17 '13 at 19:23
2

Shouldn't your NoAdditionalProcessing class be this:

public class NoAdditionalProcessing implements AdditionalProcessing<Object,Object> {

    @Override
    public void postProcess(Object o, Object o2) {   }
}

With this code, the snippets you have above all compile fine for me. warning free.

Andrew Eisenberg
  • 28,387
  • 9
  • 92
  • 148
2

You could declare your class as follows:

public class NoAdditionalProcessing<Pre, Post> 
                       implements AdditionalProcessing<Pre, Post> {
     @Override public void postProcess(Pre pre, Post post) { /*no-op*/ }
}

You can then create a new NoAdditionalProcessing<Object, Object> if you need.

assylias
  • 321,522
  • 82
  • 660
  • 783
1

You can eliminate the warning by adding an annotation to suppress it.

To get rid of the warning

@SuppressWarnings("unchecked")
public <I, O> O map(I source, Class<O> destinationClass, AdditionalProcessing<I, O> additionalProcessing) throws MappingException 
AJMansfield
  • 4,039
  • 3
  • 29
  • 50