Java Generics: infer and specify the type used in instantiating a class

For KeyHolder interface, for example:

public interface KeyHolder<K extends Key> {
    K getKey();
}

I would like to create a class as follows:

public KeyHolderSet<H extends KeyHolder<K extends Key>> extends HashSet<H> {
   public Set<K> getKeySet() {
       Set<K> keySet = new HashSet<K>();
       for (H keyHolder : this) {
           keySet.add(keyHolder.getKey());
       }
       return keySet;
   }
}

But this does not work, the closest I can get is:

public KeyHolderSet<H extends KeyHolder<? extends Key>> extends HashSet<H> {
   public <K extends Key> Set<K> getKeySet() {
       Set<K> keySet = new HashSet<K>();
       for (H keyHolder : this) {
           // Explicit cast to K
           keySet.add((K)keyHolder.getKey());
       }
       return keySet;
   }
}

How to get around this?

+3
source share
4 answers

You need to write it like this:

public KeyHolderSet<K extends Key, H extends KeyHolder<K>> extends HashSet<H> {
    public Set<K> getKeySet() {
        ...
    }
}

Unfortunately, you will have to declare type K first, and it cannot be inferred.

+2
source

Assuming that the KeyHolder implementation class that is stored in the set is not important, you can try something like this:

public class KeyHolderSet<K extends Key> extends HashSet<KeyHolder<K>> {
    public Set<K> getKeySet() {
        ...
    }
}
+3
source

public class KeyHolderSet<K extends Key, H extends KeyHolder<K>> extends
        HashSet<H> {
    public Set<K> getKeySet() {
        ...
    }
}

KeyHolderSet .

+1

, KeyHolder<K extends Key> KeyHolderSet<H extends KeyHolder<K extends Key>> :

public class KeyHolderSet<H extends KeyHolder<?>>

.

Edit: I see your problem with getKeySet(). This method should return H instead of K. H will print with what you entered in the KeyHolderSet declaration (Variabe, not the class).

public class KeyHolderSet<H extends KeyHolder<?>> extends HashSet<H> {

  private H theKeySet;

  public void setKeySet(H keyset) {
    theKeySet = keyset;
  }

  public H getKeySet() {
    return theKeySet;
  }
}

-

class betterKey extends Key {
}

-

  public static void main(String[] args) {
    KeyHolder<betterKey> kh = new KeyHolder<betterKey>() {
    };
    KeyHolderSet<KeyHolder<betterKey>> khs = new KeyHolderSet<KeyHolder<betterKey>>();
    khs.setKeySet(kh);

    KeyHolder<Key> kh2 = khs.getKeySet(); // Type mismatch
  }

As you can see, khs.getKeySet()returns KeyHolder<betterKey>as expected.

Edit2: You can create a set outside of the KeyHolderSet class:

  public static void main(String[] args) {
    KeyHolderSet<KeyHolder<betterKey>> khs = new KeyHolderSet<KeyHolder<betterKey>>();
    Set<betterKey> bks = new HashSet<betterKey>();
    for (KeyHolder<betterKey> kh : khs) {
      bks.add(kh.getKey());
    }
  }

One alternative solution that I came up with is returning a completely general set, remember that all type checking will be lost in this way.

public class KeyHolderSet<H extends KeyHolder<?>> extends HashSet<H> {
  public <K extends Key> Set<K> getKeySet() {
    Set<K> s = new HashSet<K>();
    for (H keyHolder : this) {
      s.add((K) keyHolder.getKey()); // Unchecked cast
    }
    return s;
  }

  public static void main(String[] args) {
    KeyHolderSet<KeyHolder<betterKey>> khs = new KeyHolderSet<KeyHolder<betterKey>>();
    Set<Key> ks = khs.getKeySet(); // No problem
    Set<betterKey> bks = khs.getKeySet(); // No problem
    Set<evenBetterKey> ss = khs.getKeySet(); // No problem
  }
}

class betterKey implements Key {
}

class evenBetterKey extends betterKey {
}
0
source

All Articles