Add a layer of mapping on a child class - java

I am trying to write a class that works as a type of map. The children of this class have layers of mapping on top of basic functionality. Something like the below:
public interface MyMap<K, V> {
public V get(K key);
}
public interface Client<K, V> {
public V fetch(K key);
}
public class ComplexKey<T> {
private T _key;
public ComplexKey(T key) {
_key = key;
}
T getKey() {
return _key;
}
}
public class BasicMyMap<K, V> implements MyMap<K, V> {
private final Client<K, V> _client;
public BasicMyMap(Client<K, V> client) {
_client = client;
}
#Override
public V get(K key) {
return _client.fetch(key);
}
}
/**
*
* #param <MK> mapped key
* #param <K> key
* #param <V> value
*/
public class ComplexKeyMyMap<MK extends ComplexKey, K, V> implements BasicMyMap<MK, V> {
private Function<K, MK> _mapper;
public ComplexKeyMyMap(Client<MK, V> client, Function<K, MK> mapper) {
super(client);
_mapper = mapper;
}
public V get(K rawKey) {
return super.get(_mapper.apply(rawKey));
}
}
public static void main(String[] args) {
BasicMyMap<String, String> basicMyMap = new BasicMyMap<>(key -> "success");
assert "success".equals(basicMyMap.get("testing"));
ComplexKeyMyMap<ComplexKey, String, String> complexKeyMyMap = new ComplexKeyMyMap<>(key -> "success", (Function<Object, ComplexKey>) ComplexKey::new);
assert "success".equals(complexKeyMyMap.get("testing"));
}
In addition to the key mapping, I would like to add a layer for mapping the value that is returned as well.
So question is:
What is the common approach to this problem? I have encountered this pattern multiple times and have not found a great solution.
How can I achieve this such that the users of these classes can just rely on the MyMap interface definition.
Thanks for the help.

Related

Mocking generics and wild card with mockito

I have a class that uses wild cards and generics to return a cache object and I'm trying to mock it but I get the following error:
Unfinished stubbing detected here:
-> at com.demo.MyTestTest.initTests(MyTest.java:232)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
my generic class is:
public class AsyncCaffeineCacheManager {
private final Map<String, AsyncCache<?, ?>> cacheMap;
#Getter
private final List<String> cacheNames;
private AsyncCaffeineCacheManager(Map<String, AsyncCache<?, ?>> cacheMap, List<String> cacheNames) {
this.cacheMap = cacheMap;
this.cacheNames = cacheNames;
}
#SuppressWarnings("unchecked")
public <K, V> AsyncCache<K, V> getCache(String cacheName) {
return (AsyncCache<K, V>) cacheMap.get(cacheName);
}
......
}
and my test class:
public class MyTest {
#BeforeEach
public void initTests() {
doReturn(new NoOpAsyncCache<Integer, MyValue>("cacheName"))
.when(asyncCaffeineCacheManager.getCache(anyString()));
}
}
I also created a NoOpAsyncCache which is default implementation:
#Getter
public class NoOpAsyncCache<K, V> implements AsyncCache<K, V> {
private final String cacheName;
public NoOpAsyncCache(String cacheName) {
this.cacheName = cacheName;
}
#Override
public #Nullable CompletableFuture<V> getIfPresent(K key) {
return null;
}
#Override
public CompletableFuture<V> get(K key, Function<? super K, ? extends V> mappingFunction) {
return null;
}
#Override
public CompletableFuture<V> get(K key, BiFunction<? super K, ? super Executor, ? extends CompletableFuture<? extends V>> mappingFunction) {
return null;
}
......
}
I also tried to create a real one, but it didn't work.
Would love to hear some ideas.
Turns out it was not an issue with generics.
the when(..) was wrong:
instead of:
.when(asyncCaffeineCacheManager.getCache(anyString()));
it should have been:
.when(asyncCaffeineCacheManager).getCache(anyString());
also NoOpAsyncCache didn't work well for me, had to use a real implementation
doReturn(Caffeine.newBuilder().buildAsync())
.when(asyncCaffeineCacheManager).getCache(anyString());

Why is a subclass constructor undefined in its parent class?

I have a problem here that is probably very simple to solve, but unfortunately I'm still a bit stumped.
public final class ImmutableMap<K, V> extends AbstractReadableMap<K, V>
{
public ImmutableMap(Entry<K, V>[] entry)
{
super();
}
}
I have this class ImmutableMap<K, V> and its constructor.
public interface ReadableMap<K, V>
{
public abstract ImmutableMap<K, V> asImmutableMap();
}
I have this interface ReadableMap<K, V> and its abstract method asImmutableMap.
public abstract class AbstractReadableMap<K, V> implements ReadableMap<K, V>
{
protected Entry<K, V>[] entries;
public ImmutableMap<K, V> asImmutableMap()
{
return ImmutableMap(entries);
}
}
And finally I have this class AbstractReadableMap<K, V> where I'd like to implement the method asImmutableMap(). Unfortunately I get the error The method ImmutableMap(Entry<K,V>[]) is undefined for the type AbstractReadableMap<K,V>.
public class Launcher
{
public static void main(String[] args)
{
MutableMap<String, Integer> map = new MutableMap<String, Integer>();
putEntries(map);
printEntries(map);
ImmutableMap<String, Integer> immutableMap = asImmutableMap(map);
printEntries(immutableMap);
}
}
Same here: The method asImmutableMap(MutableMap<String,Integer>) is undefined for the type Launcher
Why is that and how can I fix it?
Thanks a lot in advance!

Java - unmodifiable key set map

I am looking for a way to provide a Map with pre-defined (as in runtime immutable, not compile time const) constant key set, but modifiable values.
The JDK provides Collections.unmodifiableMap factory method, which wraps a Map and provides an immutable view of it.
Is there a similar way to wrap a Map so that only it's keys are immutable? For instance, put(K,V) will replace the value of existing keys, but throw UnsupportedOperationException if the key does not exist.
Use an enum as the key. Then one needn't care if they can add a new key since the key domain is fixed and finite. In fact, that's such a standard use case that Java provides java.util.EnumMap<K extends Enum<K>,V>
http://docs.oracle.com/javase/8/docs/api/java/util/EnumMap.html
Ok, all the solutions suggested here were to wrap or extend Collections.UnmodifiableMap. Both impossible since the original implementation would not allow to override put (and replace etc.), Which is exactly what makes it secure...
I see two options:
The "smelly" option - Using reflection to get hold of the original Map<> and directly call it's methods.
The "ugly" option - Copy the source of some of the static classes in java.lang.Collections and modify them.
If anyone has a better idea, please let me know.
Here is an initial implementation of the 2'nd solution:
import java.io.Serializable;
import java.util.*;
import java.util.function.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableSet;
/**
* #serial include
*/
public class UnmodifiableKeySetMap<K,V> implements Map<K,V>, Serializable {
private final Map<K, V> m;
/**
* Returns a view of the specified map with unmodifiable key set. This
* method allows modules to provide users with "read-only" access to
* internal maps. Query operations on the returned map "read through"
* to the specified map, and attempts to modify the returned
* map, whether direct or via its collection views, result in an
* <tt>UnsupportedOperationException</tt>.<p>
*
* The returned map will be serializable if the specified map
* is serializable.
*
* #param <K> the class of the map keys
* #param <V> the class of the map values
* #param m the map for which an unmodifiable view is to be returned.
* #return an unmodifiable view of the specified map.
*/
public static <K,V> Map<K,V> unmodifiableKeySetMap(Map<K, V> m) {
return new UnmodifiableKeySetMap<>(m);
}
UnmodifiableKeySetMap(Map<K, V> m) {
if (m==null)
throw new NullPointerException();
this.m = m;
}
public int size() {return m.size();}
public boolean isEmpty() {return m.isEmpty();}
public boolean containsKey(Object key) {return m.containsKey(key);}
public boolean containsValue(Object val) {return m.containsValue(val);}
public V get(Object key) {return m.get(key);}
public V put(K key, V value) {
if (containsKey(key)) {
return m.put(key, value);
}
throw new UnsupportedOperationException();
}
public V remove(Object key) {
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
private transient Set<K> keySet;
private transient Set<Map.Entry<K,V>> entrySet;
private transient Collection<V> values;
public Set<K> keySet() {
if (keySet==null)
keySet = unmodifiableSet(m.keySet());
return keySet;
}
public Set<Map.Entry<K,V>> entrySet() {
if (entrySet==null)
entrySet = new UnmodifiableKeySetMap.UnmodifiableEntrySet<>(m.entrySet());
return entrySet;
}
public Collection<V> values() {
if (values==null)
values = unmodifiableCollection(m.values());
return values;
}
public boolean equals(Object o) {return o == this || m.equals(o);}
public int hashCode() {return m.hashCode();}
public String toString() {return m.toString();}
// Override default methods in Map
#Override
#SuppressWarnings("unchecked")
public V getOrDefault(Object k, V defaultValue) {
// Safe cast as we don't change the value
return ((Map<K, V>)m).getOrDefault(k, defaultValue);
}
#Override
public void forEach(BiConsumer<? super K, ? super V> action) {
m.forEach(action);
}
#Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
throw new UnsupportedOperationException();
}
#Override
public V putIfAbsent(K key, V value) {
throw new UnsupportedOperationException();
}
#Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException();
}
#Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
throw new UnsupportedOperationException();
}
#Override
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
#Override
public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
#Override
public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
/**
* #serial include
*/
static class UnmodifiableSet<E> extends UnmodifiableCollection<E>
implements Set<E>, Serializable {
private static final long serialVersionUID = -9215047833775013803L;
UnmodifiableSet(Set<? extends E> s) {super(s);}
public boolean equals(Object o) {return o == this || c.equals(o);}
public int hashCode() {return c.hashCode();}
}
/**
* #serial include
*/
static class UnmodifiableCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 1820017752578914078L;
final Collection<? extends E> c;
UnmodifiableCollection(Collection<? extends E> c) {
if (c==null)
throw new NullPointerException();
this.c = c;
}
public int size() {return c.size();}
public boolean isEmpty() {return c.isEmpty();}
public boolean contains(Object o) {return c.contains(o);}
public Object[] toArray() {return c.toArray();}
public <T> T[] toArray(T[] a) {return c.toArray(a);}
public String toString() {return c.toString();}
public Iterator<E> iterator() {
return new Iterator<E>() {
private final Iterator<? extends E> i = c.iterator();
public boolean hasNext() {return i.hasNext();}
public E next() {return i.next();}
public void remove() {
throw new UnsupportedOperationException();
}
#Override
public void forEachRemaining(Consumer<? super E> action) {
// Use backing collection version
i.forEachRemaining(action);
}
};
}
public boolean add(E e) {
throw new UnsupportedOperationException();
}
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection<?> coll) {
return c.containsAll(coll);
}
public boolean addAll(Collection<? extends E> coll) {
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection<?> coll) {
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection<?> coll) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
// Override default methods in Collection
#Override
public void forEach(Consumer<? super E> action) {
c.forEach(action);
}
#Override
public boolean removeIf(Predicate<? super E> filter) {
throw new UnsupportedOperationException();
}
#SuppressWarnings("unchecked")
#Override
public Spliterator<E> spliterator() {
return (Spliterator<E>)c.spliterator();
}
#SuppressWarnings("unchecked")
#Override
public Stream<E> stream() {
return (Stream<E>)c.stream();
}
#SuppressWarnings("unchecked")
#Override
public Stream<E> parallelStream() {
return (Stream<E>)c.parallelStream();
}
}
/**
* We need this class in addition to UnmodifiableSet as
* Map.Entries themselves permit modification of the backing Map
* via their setValue operation. This class is subtle: there are
* many possible attacks that must be thwarted.
*
* #serial include
*/
static class UnmodifiableEntrySet<K,V>
extends UnmodifiableSet<Entry<K,V>> {
private static final long serialVersionUID = 7854390611657943733L;
#SuppressWarnings({"unchecked", "rawtypes"})
UnmodifiableEntrySet(Set<? extends Map.Entry<? extends K, ? extends V>> s) {
// Need to cast to raw in order to work around a limitation in the type system
super((Set)s);
}
static <K, V> Consumer<Entry<K, V>> entryConsumer(Consumer<? super Entry<K, V>> action) {
return e -> action.accept(new UnmodifiableKeySetMap.UnmodifiableEntrySet.UnmodifiableEntry<>(e));
}
public void forEach(Consumer<? super Entry<K, V>> action) {
Objects.requireNonNull(action);
c.forEach(entryConsumer(action));
}
static final class UnmodifiableEntrySetSpliterator<K, V>
implements Spliterator<Entry<K,V>> {
final Spliterator<Map.Entry<K, V>> s;
UnmodifiableEntrySetSpliterator(Spliterator<Entry<K, V>> s) {
this.s = s;
}
#Override
public boolean tryAdvance(Consumer<? super Entry<K, V>> action) {
Objects.requireNonNull(action);
return s.tryAdvance(entryConsumer(action));
}
#Override
public void forEachRemaining(Consumer<? super Entry<K, V>> action) {
Objects.requireNonNull(action);
s.forEachRemaining(entryConsumer(action));
}
#Override
public Spliterator<Entry<K, V>> trySplit() {
Spliterator<Entry<K, V>> split = s.trySplit();
return split == null
? null
: new UnmodifiableKeySetMap.UnmodifiableEntrySet.UnmodifiableEntrySetSpliterator<>(split);
}
#Override
public long estimateSize() {
return s.estimateSize();
}
#Override
public long getExactSizeIfKnown() {
return s.getExactSizeIfKnown();
}
#Override
public int characteristics() {
return s.characteristics();
}
#Override
public boolean hasCharacteristics(int characteristics) {
return s.hasCharacteristics(characteristics);
}
#Override
public Comparator<? super Entry<K, V>> getComparator() {
return s.getComparator();
}
}
#SuppressWarnings("unchecked")
public Spliterator<Entry<K,V>> spliterator() {
return new UnmodifiableKeySetMap.UnmodifiableEntrySet.UnmodifiableEntrySetSpliterator<>(
(Spliterator<Map.Entry<K, V>>) c.spliterator());
}
#Override
public Stream<Entry<K,V>> stream() {
return StreamSupport.stream(spliterator(), false);
}
#Override
public Stream<Entry<K,V>> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
public Iterator<Map.Entry<K,V>> iterator() {
return new Iterator<Map.Entry<K,V>>() {
private final Iterator<? extends Map.Entry<? extends K, ? extends V>> i = c.iterator();
public boolean hasNext() {
return i.hasNext();
}
public Map.Entry<K,V> next() {
return new UnmodifiableKeySetMap.UnmodifiableEntrySet.UnmodifiableEntry<>(i.next());
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
#SuppressWarnings("unchecked")
public Object[] toArray() {
Object[] a = c.toArray();
for (int i=0; i<a.length; i++)
a[i] = new UnmodifiableKeySetMap.UnmodifiableEntrySet.UnmodifiableEntry<>((Map.Entry<? extends K, ? extends V>)a[i]);
return a;
}
#SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
// We don't pass a to c.toArray, to avoid window of
// vulnerability wherein an unscrupulous multithreaded client
// could get his hands on raw (unwrapped) Entries from c.
Object[] arr = c.toArray(a.length==0 ? a : Arrays.copyOf(a, 0));
for (int i=0; i<arr.length; i++)
arr[i] = new UnmodifiableKeySetMap.UnmodifiableEntrySet.UnmodifiableEntry<>((Map.Entry<? extends K, ? extends V>)arr[i]);
if (arr.length > a.length)
return (T[])arr;
System.arraycopy(arr, 0, a, 0, arr.length);
if (a.length > arr.length)
a[arr.length] = null;
return a;
}
/**
* This method is overridden to protect the backing set against
* an object with a nefarious equals function that senses
* that the equality-candidate is Map.Entry and calls its
* setValue method.
*/
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
return c.contains(
new UnmodifiableKeySetMap.UnmodifiableEntrySet.UnmodifiableEntry<>((Map.Entry<?,?>) o));
}
/**
* The next two methods are overridden to protect against
* an unscrupulous List whose contains(Object o) method senses
* when o is a Map.Entry, and calls o.setValue.
*/
public boolean containsAll(Collection<?> coll) {
for (Object e : coll) {
if (!contains(e)) // Invokes safe contains() above
return false;
}
return true;
}
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Set<?> s = (Set<?>) o;
if (s.size() != c.size())
return false;
return containsAll(s); // Invokes safe containsAll() above
}
/**
* This "wrapper class" serves two purposes: it prevents
* the client from modifying the backing Map, by short-circuiting
* the setValue method, and it protects the backing Map against
* an ill-behaved Map.Entry that attempts to modify another
* Map Entry when asked to perform an equality check.
*/
private static class UnmodifiableEntry<K,V> implements Map.Entry<K,V> {
private Map.Entry<? extends K, ? extends V> e;
UnmodifiableEntry(Map.Entry<? extends K, ? extends V> e)
{this.e = Objects.requireNonNull(e);}
public K getKey() {return e.getKey();}
public V getValue() {return e.getValue();}
public V setValue(V value) {
throw new UnsupportedOperationException();
}
public int hashCode() {return e.hashCode();}
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> t = (Map.Entry<?,?>)o;
return eq(e.getKey(), t.getKey()) &&
eq(e.getValue(), t.getValue());
}
public String toString() {return e.toString();}
}
}
/**
* Returns true if the specified arguments are equal, or both null.
*
* NB: Do not replace with Object.equals until JDK-8015417 is resolved.
*/
static boolean eq(Object o1, Object o2) {
return o1==null ? o2==null : o1.equals(o2);
}
}
I know this isn't strictly what was asked in the question, but I think this approach is worth considering.
Use Google Guava's ImmutableMap and make your value type mutable- with a wrapper type if necessary.
Before moving on, it's important to understand this: immutability at the map level only means you can't re-assign objects. There's nothing stopping you from calling mutator methods on the objects already in the map. With this insight, the above approach might be obvious, but let me explain via an example.
For me, the value type was AmtomicInteger- already perfectly mutable in exactly the right way I needed, so I didn't need to change anything. However, in general, let's say you have a type T, and you want a partially mutable map for this type. Well, you can almost do this via a mutable map whose values are wrapper types for that class. Here's a really basic wrapper type, which you might want to enhance with some encapsulation:
public class Wrapper<T>{
public T value;
Wrapper(T t){value = t;}
}
Then you simply create a regular ImmutableMap in the normal way, except fill it with objects of Wrapper instead of objects of T. When using the values, you need to unbox them from the wrapper.
Again, I know this isn't what was asked, and unboxing from a wrapper might be too painful for some use cases, but I'd imagine this would work in a lot of cases. Certainly for me, it helped me realise that I already had a mutable type that was basically a wrapper for an int, and so the ImmmutableMap was fine.
1) The proxy
I would reduce the scope of your SemiMutableMap to something like
interface ISemiMutableMap<U, V> {
V get(U key);
V set(U key, V value) throws Exception; //create your own maybe ?
}
This will reduce the possibilities of access but give you the full control of it.
And then implements it simply like a proxy
public class SemiMutableMap<U, V> implements ISemiMutableMap<U,V>{
private Map<U, V> map;
public SemiMutableMap(Map<U, V> map){ //get the predefine maps
this.map = map;
}
public V get(U key){
return map.get(U);
}
public V set(U key, V value) throws Exception{
if(!map.containsKey(key)){
throw new Exception();
}
return map.put(key,value);
}
}
And you can add the methods you like to it off course.
Note that this is not complety true, The constructor should clone the map instead of using the same reference but I am a bit lazy ;) and I've writen this without an IDE
2) The implementation
Nothing prevent you to simply get the code of the UnmodifiableMap from the Collections and adapt it to your needs. From this, you will see it is quite simple to create your own implementation to your need.
Trust me, this class as been tested and reviewed ;)
You will need to adapt put to be able to update an existing value (same code as above) and UnmodifiableEntry.setValue to accept an update from the entry set.
I believe you want to do something like this.
public class UnmodifiableKeyMap{
static enum MYKeySet {KEY1, KEY2, KEY3, KEY4};
private Map myUnmodifiableHashMap = new HashMap();
public boolean containsKey(Object key) {
return this.containsKey(key);
}
public Object get(Object key) {
if(this.containsKey(key)){
return this.myUnmodifiableHashMap.get(key);
}else {
return null;
}
}
public Object put(Object key, Object value) throws Exception {
if(this.containsKey(key)){
this.myUnmodifiableHashMap.put(key, value);
}
throw new Exception("UnsupportedOperationException");
}
public Set keySet() {
Set mySet = new HashSet(Arrays.asList(MYKeySet.values()));
return mySet;
}
public Collection values() {
return this.myUnmodifiableHashMap.values();
}
}

Java Generics: Infer and reference a type used in the class instantiation

Given a KeyHolder interface such as the following:
public interface KeyHolder<K extends Key> {
K getKey();
}
I'd like to create a class like this:
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 that doesn't work, the closest I can get is this:
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;
}
}
Any way around this?
Assuming the implementation class of the KeyHolder that is stored in the set is not important, you could try something like this:
public class KeyHolderSet<K extends Key> extends HashSet<KeyHolder<K>> {
public Set<K> getKeySet() {
...
}
}
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 the type of K first and it cannot be inferred.
Maybe
public class KeyHolderSet<K extends Key, H extends KeyHolder<K>> extends
HashSet<H> {
public Set<K> getKeySet() {
...
}
}
if you don't mind parameterizing KeyHolderSet twice.
Given that KeyHolder<K extends Key> it seems unnecessary to use KeyHolderSet<H extends KeyHolder<K extends Key>> Just stick with:
public class KeyHolderSet<H extends KeyHolder<?>>
and you should be ok.
Edit: I see your problem with getKeySet(). This method should return H instead of K. H will be typed with what you entered in the declaration of KeyHolderSet (The 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 build up the set outside 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 alternate solution i came up with is returning a completely generic Set, remember all type checking will be lost 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 {
}

Can generic XmlAdapter be written

I know, that I can use Raw types to write XMLAdapter, but can I use generic types. I tried reading the API ( link ), but did not even notice a clue about this.
For example map:
I want to use, something like:
#XmlJavaTypeAdapter(GenericMapAdapter<String, Double>.class)//
private final HashMap<String, Double> depWageSum = //
new HashMap<String, Double>();
to get
<depWageSum>
<entry key="RI">289.001</entry>
<entry key="VT">499.817</entry>
<entry key="HI">41.824</entry>
...
<depWageSum>
And class itself would probably look something in the lines of:
#SuppressWarnings("serial") public class GenericMapAdapter<K, V> extends XmlAdapter<GenericMapAdapter.MapType<K, V>, Map<K, V>> {
public static class MapType<K, V> {
#XmlValue protected final List<MapTypeEntry<K, V>> entry = new ArrayList<MapTypeEntry<K, V>>();
public static class MapTypeEntry<K, V> {
#XmlAttribute protected K key;
#XmlValue protected V value;
private MapTypeEntry() {};
public static <K, V> MapTypeEntry<K, V> of(final K k, final V v) {
return new MapTypeEntry<K, V>() {{this.key = k; this.value = v;}};
} } }
#Override public Map<K, V> unmarshal(final GenericMapAdapter.MapType<K, V> v) throws Exception {
return new HashMap<K, V>() {{ for (GenericMapAdapter.MapType.MapTypeEntry<K, V> myEntryType : v.entry)
this.put(myEntryType.key, myEntryType.value);}};
}
#Override public MapType<K, V> marshal(final Map<K, V> v) throws Exception {
return new GenericMapAdapter.MapType<K, V>() {{for (K key : v.keySet())
this.entry.add(MapTypeEntry.of(key, v.get(key)));}};
} }
You will not be able to do this as described. The type parameters will not be retained by the class. However you could introduce some simple subclasses that could leverage the logic from your GenericMapAdapter:
public class StringDoubleMapAdapter extends GenericMapAdapter<String, Double> {
}
Then use the adapter sub class on the property:
#XmlJavaTypeAdapter(StringDoubleMapAdapter.class)//
private final HashMap<String, Double> depWageSum = //
new HashMap<String, Double>();
For more information on XmlAdapter see:
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html

Categories

Resources