Pergunta

I want to implement the Map interface, but bound K and V to interfaces). However given that the Java collections API, and specifically the MAP interface restricts the method parameters at a minimum, using Object instead of the generic types K and V. To exemplify the interface specifies: public boolean containsValue(Object value) instead of public boolean containsValue(V value). I can not rely on compiler type safety in such occasions.

To be clear, i want to implement something like the following:

class MyMap< K , V extends ValueInterface> implements Map<K,V>

However, I am required to implement methods like the following:

@Override
public boolean containsValue(Object value) {
      // What to do here? 
      ValueInterface v = (ValueInterface ) v; 
      v.getWhatIWant().andDoThing(); 
      // Follow on... 
}

Which options and/or best practices I have in this situation?

Foi útil?

Solução

This is the interface contract of Map - as such you must implement the contract as it stands.

However, let me suggest two reasons why the design of this part of the Map is a good thing:

i) The Map contract is that you can get values from the map under the following circumstances:

if this map contains a mapping from a key k to a value v such that (key==null ? k==null : key.equals(k)), then this method returns v; otherwise it returns null. (There can be at most one such mapping.)

or equivalently, and specific to your question about containsKey(Object key):

Returns true if this map contains a mapping for the specified key. More formally, returns true if and only if this map contains a mapping for a key k such that (key==null ? k==null : key.equals(k)). (There can be at most one such mapping.)

As such, you are not concerned about typing, but about equality.

ii) This is really just the same point, but consider the following:

Map<String, Integer> map = new HashMap<>();
String key = "A Key";
Integer value = 1;
map.put(key, value);

Object objectKey = key; // this is the same key object, but it is typed as Object
map.containsKey(objectKey); // what would you want/expect this to return?
map.get(objectKey); // ...or this?

As such, with regard to your implementation of Map, I would not advise just casting the Object key (you'll get a runtime exception...). How you decide to implement it will depend on your design. Let us imagine that you're storing the keys and values in typed lists... then you would be advised to check the instanceof the Object key before casting:

public boolean containsKey(Object key) {
  if (key instanceof K) {
    // do your thing...
  } else {
    return false;
  }
}

Note that instanceof checks the type of the underlying object, not its declared type, so:

String key = "A Key";
Object objectKey = key;
boolean isString = (objectKey instanceof String); // is true

Really, I would suggest that rather than asking how to force something that goes against the Map contract, you want to be asking the question "why is the contract that way?"...

Outras dicas

If you want to implement Map<K,V> you will be required to implement all methods in the interface.

You could extend AbstractMap<K,V> and override the abstract methods, plus any non-abstract methods for which you want custom behavior (as long as you don't violate the Map contract).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top