0

I like to have a java.util.Map, which holds different generic class objects. In my case handlers such as this:

public interface Handler<s>{
 public void encode (S data, OutputStream out);
 public S decode (InputStream in, long length);
}

I have implementing classes such as:

public class SpecializedHandler implements Handler<FirstSpecialItem>{
 public void encode (SpecialItem data, OutputStream out){
  // do something
 }
 public SpecialItem decode(InputStream in, long length){
  // do something
 }
}

and another class which implements Handler.

For FirstSpecialItem and SecondSpecialItem exists a parent class AbstractSpecialItem.

Because I had a lot of problems using encode and decode already, I ended up with the following map, which enables to use of decode and encode as follows:

Map<Long, Handler<? super AbstractSpecialItem>> handlers;
// I can use it as follows
AbstractSpecialItem item = new FirstSpecializedItem();

handlers.get(1L).encode(item,System.out);   
AbstractSpecialItem returnVal = handlers.get(1L).decode(System.in, 100L);

However, I can't create the map by adding different Handler implementations such as:

SpecializedHandler a = new SpecializedHandler();
SpecializedSecondHandler b = new SpecializedSecondHandler();
Map<Long, Handler<? super AbstractSpecialItem>> handlers = new HashMap<Long,Handler<? super AbstractSpecialItem>>();
handlers.put(0L, a); // does not work
handlers.put(1L, b); // does not work 

Eclipse always says these values are not applicable to the Map. I think this behaviour is valid and tried to understand Generics and the PECS principle better (looked through the web and some books). But I still don't get it, to figure out a solution. Where I have the Handler classes with their special generic types and be able to use the methods (encode,decode) as well as create an Map of Handlers.

2 Answers2

0

It should work if you use Map<Long, Handler<? extends AbstractSpecialItem>> instead of Map<Long, Handler<? super AbstractSpecialItem>>. Note the extends instead of super, for a more detailed explanation, I suggested you look at the answers for this question: java generics super vs. extends.

Here's a working example:

import java.io.*;
import java.util.*;

public class Test {
  public abstract class A<T extends Closeable> { }
  public class B<T extends Closeable> extends A<T> { }
  public class C<T extends Closeable> extends A<T> { }

  public Map<String, A<? extends Closeable>> map = new HashMap<>();
  // instance initializer
  {
    map.put("1", new B<InputStream>());
    map.put("2", new C<Reader>());
  }
}
Community
  • 1
  • 1
Lolo
  • 3,890
  • 2
  • 20
  • 22
0

In your case you should use

Map<Long, Handler<? extends AbstractSpecialItem>> handlers = new HashMap<Long, Main.Handler<? extends AbstractSpecialItem>>();

you can read about the difference between super and extends in here: Difference between <? super T> and <? extends T> in Java

Community
  • 1
  • 1
Nemo
  • 537
  • 5
  • 12
  • @Lolo and Nemo, thank you for your fast replay. Yes that is correct, but as far as I have tried it, if I use extends, I am not able to use the encode-Methode anymore. Eclipse shows the following failure, if I try to invoke it, as follows: `handlers.get(1L).encode(item,System.out);` `handlers.get(1L).encode((AbstractSpecialItem)item,System.out);` – user2444232 Aug 31 '14 at 21:22
  • @Lolo and Nemo, thank you for the fast reply. Yes, but if I use extends, I can't use the encode-Methode. Eclipse shows the following failure, if invoked: `handlers.get(1L).encode(item,System.out);` `handlers.get(1L).encode((AbstractSpecialItem)item,System.out);`_Error: The method encode(capture#2-of ? extends AbstractSpecialItem, OutputStream) in the type Handler is not applicable for the arguments (AbstractSpecialItem, PrintStream)._ With concrete Item, it's the same stating e.g. _FirstSpecializedItem_. The item in encode seems of type null. – user2444232 Aug 31 '14 at 21:31
  • I'm not sure I understand your use case. Let me ask you this; as a developer you need to pass encode a parameter. When you get a Handler form the map, how do you know the handler's type? You need to know the type for calling encode method (use FirstSpecialItem or SecondSpecialItem). – Nemo Sep 03 '14 at 13:39
  • I know the type from the key I used as index for the handler in the map (not my idea). However the key is not the type's class-object its just a well known index for the type. At the moment I found my solution using raw types and reader and writer classes instead of a serializer class which does both. Now I have less problems with the Generics PECS principle and it occured to me, that this is better in terms of the single responsibility principle. – user2444232 Oct 19 '14 at 09:38
  • However it is not quite the answer to my first problem, it is a workaround. – user2444232 Oct 19 '14 at 09:45