How can I bind parameter type values ​​in the interface of a Java map?

I do not think the title of the question will be clear, but the idea is simple.

Suppose I have a map type variable.

Map<K,V> myMap; 

but I want to establish a connection between K and V. For example, I would like to say that this map connects the sets of one class with objects of this class. Sort of:

 Map<Set<T>, T> myMap; 

but not for a specific type T. I would like this Card to accept records like

 (Set<String>, String), (Set<Integer>, Integer) ... 

Is there a possible declaration for myMap that allows me to have this behavior? Please let me know if I misunderstand or if I have a previous conceptual error.

+4
source share
4 answers

Unfortunately, this is not possible with Java generics. If Java allows higher order type parameters, then Map could be defined as something like:

 public interface Map<V<>> { // here V<> is my hypothetical syntax for a // type parameter which is itself generic... <K> V<K> put(K key, V<K> value); ... } 

instead of the actual java.util.Map :

 public interface Map<K, V> { V put(K key, V value); ... } 

You can see that the problem is that K declared once for the whole class, and not for every .put() call.

Enough to fantasize, so what can you do? I think it is best to create a Map<Set<?>, Object> and wrap it as a private member. Then you can create your own put() and get() , which take into account the supposed "relationship" between the types:

 class SpecialMap { private Map<Set<?>, Object> map = ...; public <T> T put(Set<T> key, T value) { return (T) map.put(key, value); } public <T> T get(Set<T> key) { return (T) map.get(key); } } 
+2
source

What you are trying to do does not seem like a good skill, because each Set<T> always not equal to another Set<T> , even if of the same type - using Sets as keys is more or less useless.

However, you do not need to define a new class - you can require a method to accept such a card:

 public static <T> void process(Map<Set<T>, T> map) { for (Map.Entry<Set<T>, T> entry : map) { Set<T> key = entry.getKey(); T value = entry.getValue(); // do something } } 
+1
source

There is no way with generators so that the compiler can check a different T for each put() call. In other words, you cannot have the same card and do:

 myMap.put(new HashSet<String>(), "foo"); myMap.put(new HashSet<Integer>(), 1); 

If you need this, you may need to save the <Object> and perform the check yourself using instanceof or another hack.

Now you can do something like:

 public class MyMap<T> extends HashMap<Set<T>, T> { ... 

Then you can do:

 MyMap<String> myMap = new MyMap<String>(); Set<String> set = new HashSet<String>(); myMap.put(set, "foo"); 

Remember that the key must have valid hashCode() and equals() methods, which can be expensive with Set .

0
source

I don’t think it is possible to do compile-time checks using Java generics. However, it is quite simple at runtime. Just a short decorator on the right:

 public class FancyTypeMapDecorator implements Map<Set<? extends Object>, Object> { final Map<Set<? extends Object>, Object> target; public FancyTypeMapDecorator(Map<Set<? extends Object>, Object> target) { this.target = target; } @Override public Object put(Set<? extends Object> key, Object value) { final Class<?> keyElementType = key.iterator().next().getClass(); final Class<?> valueType = value.getClass(); if (keyElementType != valueType) { throw new IllegalArgumentException( "Key element type " + keyElementType + " does not match " + valueType); } return target.put(key, value); } @Override public void putAll(Map<? extends Set<? extends Object>, ? extends Object> m) { for (Entry<? extends Set<? extends Object>, ? extends Object> entry : m.entrySet()) { put(entry.getKey(), entry.getValue()); } } //remaining methods are simply delegating to target } 

Here's how it works:

 final Map<Set<? extends Object>, Object> map = new FancyTypeMapDecorator(new HashMap<Set<? extends Object>, Object>()); Set<? extends Object> keyA = Collections.singleton(7); map.put(keyA, 42); Set<? extends Object> keyB = Collections.singleton("bogus"); map.put(keyB, 43); 

The second put throws an exception.

However, the implementation (and I don’t even mean that it will be unsuccessful for an empty Set as a key), and using the / API starts an alarm ... Do you really want to deal with such a structure? Maybe you need to rethink your problem? What are you really trying to achieve?

0
source

Source: https://habr.com/ru/post/1380645/


All Articles