7 Replies Latest reply on Jul 22, 2010 4:03 PM by 843793

    Interface inheritance and Generics

    843793
      Hello!

      For an application I need several 'stores' (classes which contain some elements) which share certain characteristics. I would like to create a general StoreInterface, and some specialized interfaces which inherit from this, e.g. a 'KwicStoreInterface'.
      Also I would like to specify that the Store takes elements of a general type 'Storable', but the KwicStore only takes objects of the type 'KwicInterface' which inherits from 'Storable'.
      I thought that specifying these restrictions using Generics would be a good idea, but I can't get the hang of it.

      This is a simplyfied version of what I tried:
      // interfaces for the stores
      
      public interface StoreInterface<T extends Storable>{
          public T getElem(Long id);
      
          public void setElem(T elem);
      
          public void setElems(Map<Long, T> elems);
      
      }
      
      public interface KwicStoreInterface<T extends KwicInterface> extends StoreInterface{
        
          public void SomeKwicStoreSpecificMethod();
      
      }
      
      // Interfaces for the elements
      
      public interface Storable {
          public Long getID();
      
          public void setID(Long id);
      }
      
      public interface KwicInterface extends Storable{
          public void SomeKwicSpecificMethod();
      }
      When I try to instantiate KwicStoreInterface, this code doesn't compile, because the methods are not recognized as valid implementations of the interface's methods.
      public KwicStore<T extends KwicInterface> implements KwicStoreInterface {
           private Map<Long, T> elements = new HashMap<Long, T>();
      
         // Only his method seems to be recognized correctly:
          public T getElem(Long id){
                return elements.get(id);
          }
      
         // not recognized as interface method
          public void setElem(T elem){
                 elements.put(elem.getID(), elem);
          }
      
          // not recognized as interface method
          public void setElems(Map<Long, T> elems){
                  elements = elems;
          }
      
         // this one seems okay, too, of course:
         public void SomeKwicStoreSpecificMethod() {}
      
      }
      If I allow the Netbeans IDE to automatically instantiate the methods, all the type information is reverted to Storable or is lost completely (in case of the Map), resulting in methods like:
      @Override
          public void setElem(Storable elem) {
              throw new UnsupportedOperationException("Not supported yet.");
          }
      
          @Override
          public void setElems(Map elems) {
              throw new UnsupportedOperationException("Not supported yet.");
          }
      
          @Override
          public Storable getElem(Long id) {
              throw new UnsupportedOperationException("Not supported yet.");
          }
      What am I doing wrong and how can I achieve the functionality I want?
      Thanks in advance for any help!
        • 1. Re: Interface inheritance and Generics
          jtahlborn
          you forgot the generics specifier on the StoreInterface.
          public interface KwicStoreInterface<T extends KwicInterface> extends StoreInterface<T> {
            
              public void SomeKwicStoreSpecificMethod();
           
          }
          • 2. Re: Interface inheritance and Generics
            843793
            Thanks, that was really helpful!

            However, I ran into new problem now.
            The KwicStore should be built using a Reader object (which parses an XML file) and the reader contains a Map with concrete types.
            However, it seems impossible to initialize a 'variable parameter' with a concrete type.
            public class KwicStore<T extends KwicInterface> implements KwicStoreInterface<T>{
            
                Map<Long, T> elements;
            
                // doesn't work:
                KwicStore() {
                    KwicStoreReader reader = new KwicStoreReader();
                    reader.read();
                    this.elements = reader.readerElems;
                }
            
               // other methods
            }
            
            // the very simplified reader
            
            public class KwicStoreReader {
                 public Map<Long, KwicInterface> readerElems = new HashMap<Long, KwicInterface>();
            
                // do something to fill readerElems with Kwic objects (Kwic instantiates KwicInterface)
                public void read() {
                     KwicInterface kwic = new Kwic();
                     readerElems.put(kwic.getID(), kwic);
                }
            
            }
            Is there any way around this?
            I already tried to use Generics and variables within the reader, too, but then I couldn't put Kwic objects into the readerElems map.
            public class KwicStoreReader<T extends KwicInterface> {
                 public Map<Long, T> readerElems = new HashMap<Long, T>();
            
                // do something to fill elems with Kwic objects (Kwic instantiates KwicInterface)
                public void read() {
                     T kwic = new Kwic(); // this doesn't work
                     readerElems.put(kwic.getID(), kwic);
                }
            
            }
            Actually, I would also be fine with restricting KwicStoreInterface to take KwicInterface objects only (without the extends), but I don't know how to declare that correctly.
            • 3. Re: Interface inheritance and Generics
              843793
              reed444 wrote:
              I already tried to use Generics and variables within the reader, too, but then I couldn't put Kwic objects into the readerElems map.
              You can make KwicStoreReader abstract, along with the read() method, and then have a concrete implementation for each type of KwicInterface. Or, better, have your KwicStoreReader take an abstract factory as a parameter:
              public interface KwicFactory<T extends KwicInterface> {
                  public T create();
              }
              
              public class KwicStoreReader<T extends KwicInterface> {
                   public Map<Long, T> readerElems = new HashMap<Long, T>();
                   private final KwicFactory<T> factory;  //make sure you assign this in your constructor
                   
                  public void read() {
                       T kwic = factory.create();
                       readerElems.put(kwic.getID(), kwic);
                  }
               
              }
              • 4. Re: Interface inheritance and Generics
                jtahlborn
                or configure KwicStoreReader with a "class<T>" instance.
                • 5. Re: Interface inheritance and Generics
                  843793
                  Very interesting, but... how does the implementation of that interface look like? I could think of
                  public class KwicFactoryImpl implements KwicFactory<Kwic> {
                  
                      @Override
                      public Kwic create() {
                          return new Kwic();
                      }
                  
                  }
                  ... but that sort of defeats the purpose, doesn't it?
                  • 6. Re: Interface inheritance and Generics
                    843793
                    reed444 wrote:
                    Very interesting, but... how does the implementation of that interface look like? I could think of
                    public class KwicFactoryImpl implements KwicFactory<Kwic> {
                    
                    @Override
                    public Kwic create() {
                    return new Kwic();
                    }
                    
                    }
                    ... but that sort of defeats the purpose, doesn't it?
                    It doesn't defeat the purpose at all. You've provided a factory which creates a Kwic. Note that you could do this as an anonymous inner class however, which might be a bit nicer in your code.

                    As far as this specific example as concerned, you might be able to pass in a Class<? extends T> as jtahlborn suggested, since your constructor for Kwic takes no arguments. For classes that have a no-argument constructor, a Class object acts as a factory via the newInstance method.
                    • 7. Re: Interface inheritance and Generics
                      843793
                      Okay, I did manage to get the project compiled now without any errors and warnings. I'm still a little doubtful if it does exactly what I want and you may find me back here with new problems in a bit, but for the moment I guess I can declare the question answered.

                      Thanks a lot for your help!