ha9u63a7
ha9u63a7

Reputation: 6862

Java Generics with Composite Key and Wildcards

I have a pair interface e.g.

public interface CompositeKeyType<K1, K2> {
    public K1 getKey1();
    public K2 getKey2();
}

And an implementation:

package com.bcsg.creditcardrecords;

public class CompositeKeyImplementer<K1, K2> implements
    CompositeKeyType<K1, K2> {

    private K1 key1;
    private K2 key2;

    public CompositeKeyImplementer() {
        this.key1 = null;
        this.key2 = null;
    }

    public CompositeKeyImplementer(K1 key1, K2 key2) throws IllegalArgumentException {
        if (key1.equals(key2)){
            throw new IllegalArgumentException("both keys cannot be equal");
        }
        this.key1 = key1;
        this.key2 = key2;
    }

     @Override
     public K1 getKey1() {
        return this.key1;
    }

    @Override
    public K2 getKey2() {
        return this.key2;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof CompositeKeyImplementer<?, ?>)) {
            return false;
        }

        if (!(((CompositeKeyImplementer<?, ?>)     obj).getKey1().equals(this.key1))
            || !(((CompositeKeyImplementer<?, ?>) obj).getKey2()
                    .equals(this.key2))) {
            return false;
        }
        return true;
    }
}

Now....I also have an abstract class:

public abstract class AbstractBankCardDetailsHolder<K, V> {

    private NavigableMap<K, V> cardData;

    public AbstractBankCardDetailsHolder() {
        cardData = new TreeMap<K, V>();
    }

    public AbstractBankCardDetailsHolder(K key, V value){
        cardData.put(key, value);
    }

    public NavigableMap<K, V> getCardData(){
        return this.cardData;
    }

    public void setCardData(NavigableMap<K,V> cadData){
        this.cardData.clear();
        this.cardData.putAll(cardData);
    }              
}

Which I am generalising here (it is coming up with errors):

public class CompositeKeyBasedCreditCardDetailsHolder<? extends K, ? extends V> extends
                AbstractBankCardDetailsHolder<? extends K, ? extends V> {

            private CompositeKeyImplementer<? extends K, ? extends K> numberProviderPair;

            // ....... TBC

}      

I was under the impression that ? wildcard means ?TypeUnknown? and it will resolve the types @Runtime. However, I notices whilsts writing this questiont that my CompositeKeyImplementer.java class has got wildcards in the equals method too. Is this something that I won't be able to achieve because JVM cannot resolve the different wildcard arrangements such as this one during runtime?

Upvotes: 0

Views: 173

Answers (1)

Andy Brown
Andy Brown

Reputation: 19171

From what I can work out from your sample code:

1) Your CompositeKeyImplementer needs to be generic. It implements a generic interface, and you later refer to it as a generic type.

public class CompositeKeyImplementer<K1, K2> implements CompositeKeyType<K, V> {
  ...

2) You want to have a CompositeKeyImplementor<K1, K2> with any type arguments that are subtypes of K as a field in your CompositeKeyBasedCreditCardDetailsHolder class.

Therefore you use the wildcards on invocation of that CompositeKeyImplementor as type arguments. You don't use them as type parameters in the generic type declaration for CompositeKeyBasedCreditCardDetailsHolder

public class CompositeKeyBasedCreditCardDetailsHolder<K, V> extends
AbstractBankCardDetailsHolder<K, V> {

    private CompositeKeyImplementer<? extends K, ? extends K> numberProviderPair;

    // ....... TBC

}

With this you are saying:

  1. I am declaring a generic CompositeKeyBasedCreditCardDetailsHolder with type parameters K, V.
  2. That type has a field called numberProviderPair
  3. Which itself is a generic type CompositeKeyImplementer<K1, K2>
  4. and in fact which itself can be any CompositeKeyImplementer<K1, K2> where the type parameters K1, K2 are a subtype (inclusive) of K
  5. i.e. they are upper bounded by the type parameter K defined by CompositeKeyBasedCreditCardDetailsHolder

Note that the type arguments for K1, K2 are not restricted to the same type. For example: this is possible:

// note the arguments for K1, K2. Both extend Number
CompositeKeyImplementer<Integer, Double> cki = 
    new CompositeKeyImplementer<Integer, Double>();
// note the argument for K is Number
CompositeKeyBasedCreditCardDetailsHolder<Number, String> cdh = 
    new CompositeKeyBasedCreditCardDetailsHolder<Number, String>(cki);

I recommend (re-)reading the java tutorials on wildcards, and possibly also Angelika Langer on wildcard type arguments as they have a lot of information on what wildcards are used for and how.

Upvotes: 1

Related Questions