This is a shortened sample of my code to prove my point:
public class Tmp {
static class X {
void setStr(String blah) {
}
String getStr() {
return null;
}
}
public void test() {
createCheck(X::getStr, ""); // supposed to compile
createCheck(X::getStr, 123); // rather not: int isn't String
}
private <T, V> BiPredicate<T, String> createCheck(Function<T, V> func, V value) {
return new BiPredicate<T, String>() {
#Override
public boolean test(T t, String ref) {
assertThat(func.apply(t))
.as(ref)
.isEqualTo(value);
return true;
}
};
}
}
IMHO, the compiler should see that V in createCheck() should be String coming from the getter-function, which is why it should complain about the int. Or the other way around.
So why does this compile?
Your signature is:
private <T, V> BiPredicate<T, String> createCheck(Function<T, V> func, V value) {
You have no bounds on T or V so absolutely any values of T or V would compile.
You return a BiPredicate<T, String> however, so the isEqualTo method takes the String ref and compares its equality to the Object value - this is also perfectly legal as I suppose isEqualTo takes an Object to mimic the behaviour of Object.equals.
In your example:
createCheck(X::getStr, 123);
V is simply Object as that is the tightest type bound that can apply to String and Integer.
Did you perhaps mean:
private <T> BiPredicate<T, String> createCheck(Function<T, String> func, String value) {
Related
I have a "Schema" and "Field" model in which a Field represents a data type and has methods on how to parse it, and the schema is a collection of fields. I am trying to implement generic comparison, however, I can't get the code to compile and I can't figure out the proper generic scopes. How can I get this to work?
class Field<T extends Comparable<? super T>> {
T parse(String val) {
...
}
}
public class Schema {
Map<Integer, Field<?>> fields;
Field<?> getField(int index){ ... }
}
public class Comparison {
public static <T extends Comparable<? super T>> boolean greaterThan(Field<T> f, String val1, String val2) {
// compiles as expected
return f.parse(val1).compareTo(f.parse(val2)) > 0;
}
public static boolean greaterThan2(Field<?> f, String val1, String val2) {
// does not compile -> required capture of ? super capture of ?, provided capture of ?
return f.parse(val2).compareTo(f.parse(val2));
}
}
public class App {
public static void main(String[] args) {
Schema s = ...
// does not compile, required type Field<T>, provided type Field<capture of ?>
Comparison.greaterThan(s.getField(0), "val1", "val2");
// compiles
Comparison.greaterThan2(s.getField(0), "val1","val2");
}
}
Below code compiles in 1.8
import java.util.Map;
class Field<T extends Comparable<? super T>> {
T parse(String val) {
return null;
}
}
class Schema {
Map<Integer, Field<?>> fields;
Field<?> getField(int index){ return null; }
}
class Comparison {
public static <T extends Comparable<? super T>> boolean greaterThan(Field<T> f, String val1, String val2) {
// compiles as expected
return f.parse(val1).compareTo(f.parse(val2)) > 0;
}
}
public class App {
public static void main(String[] args) {
Schema s = new Schema();
//compiles ok
Comparison.greaterThan(s.getField(0), "val1", "val2");
}
}
I am creating a 'SpecialList' and am require to implement the map function. The list should be lazy, and will only produce values when evaluated.
toString() returns "?" if the value is not yet available; returns the string representation of the value otherwise.
get() called when the contents is needed. If the value is already available, return that value; otherwise, compute the value and return it. The computation should only be done once for the same value.
Here's what I have:
public <U> SpecialList<U> map(Function<T, U> mapper) {
if (!this.isAvailable) {
return new SpecialList<U>(this.supplier);
}
return new SpecialList<U>(mapper, value);
}
// private constructor
private SpecialList(CachedSupplier<T> s) {
this.supplier = s;
this.isAvailable = false;
}
However, it is telling me that there's no valid constuctor because T cannot be converted to U.
SpecialList.java:65: error: no suitable constructor found for SpecialList(SpecialList<T>.CachedSupplier<T>)
return new SpecialList<U>(this.supplier);
^
constructor SpecialList.SpecialList(U) is not applicable
(argument mismatch; SpecialList<T>.CachedSupplier<T> cannot be converted to U)
constructor SpecialList.SpecialList(Supplier<U>) is not applicable
(argument mismatch; SpecialList<T>.CachedSupplier<T> cannot be converted to Supplier<U>)
Doesn't the 'U' become a T when returned?
How do I go about solving this? I am still a little unclear about the method level generic types. But I was told that I need to add the < U > for my map method.
Below is my code in full:
class SpecialList<T> {
class CachedSupplier<T> {
private Supplier<? extends T> supplier;
private T value;
boolean isAvailable;
public CachedSupplier(Supplier<? extends T> supplier) {
this.supplier = supplier;
}
public T get() {
if (!isAvailable) {
value = supplier.get();
isAvailable = true;
}
return value;
}
}
private CachedSupplier<T> supplier;
private T value;
boolean isAvailable;
private SpecialList(T value) {
this.value = value;
this.isAvailable = true;
}
private SpecialList(Supplier<T> s) {
this.supplier = new CachedSupplier<T>(s);
this.isAvailable = false;
}
private SpecialList(CachedSupplier<T> s) {
this.supplier = s;
this.isAvailable = false;
}
private <R> SpecialList(Function<T, R> mapper, T v) {
this.supplier = new CachedSupplier<T>(() -> mapper.apply(v));
this.isAvailable = false;
}
public static <T> SpecialList<T> of(T value) {
return new SpecialList<>(value);
}
public static <T> SpecialList<T> of(Supplier<T> supplier) {
return new SpecialList<>(supplier);
}
public <R> SpecialList<R> map(Function<? super T,? extends R> mapper) {
if (!this.isAvailable) {
return new SpecialList<>(this.supplier);
}
return new SpecialList<R>(mapper, value);
}
public T get() {
if(this.isAvailable) {
return this.value;
} else {
this.value = this.supplier.get();
this.isAvailable = true;
return this.value;
}
}
}
I am still a little confused with Generic Types etc. so please let me know if there's anything odd/I can improve!
Thanks
According to the code you posted, there is a compile-time error in one of the constructors for class SpecialList...
private <R> SpecialList(Function<T, R> mapper, T v) {
this.supplier = new CachedSupplier<T>(() -> mapper.apply(v));
this.isAvailable = false;
}
Firstly, in the code you posted, there is no constructor in inner class CachedSupplier that takes a Function parameter, so you need to add one with this signature:
public <R> CachedSupplier(Function<T, R> mapper)
And the second problem with the SpecialList constructor is the lambda expression. Abstract method apply in interface Function has a parameter, which your lambda expression is missing. So the constructor code should be:
private <R> SpecialList(Function<T, R> mapper, T v) {
this.supplier = new CachedSupplier<T>((v) -> mapper.apply(v));
this.isAvailable = false;
}
The problem is your map function is changing the SpecialList type parameter from T to R. But in the fragment highlited by compiler you return still SpecialList.
Things you can do:
If you are not planning to change the type of elements with your map() function, get rid of R, and replace Function<? super T,? extends R> mapper with Function<? super T,? extends T> mapper. However, this way the mapper would be totaly omited, which might not be the desired behaviuor.
Otherwise, you should pass into your SpecialList constructor a combination of the original supplier and new mapper like so:
return new SpecialList<R>(() -> {
T originalValue = this.supplier.get();
return mapper.apply(originalValue);
};
I think you're doing an overkill by checking isAvailable in both, the SpecialList and the CachedSupplier. It seems enough to me to do so in the CachedSupplier and reduce the members of SpecialList to just the supplier (which is, when the value is available, simply a value-getter without additional costly evaluations). And in the constructors you make sure that the supplier is a cached one, when needed (i.e. when you don't have the value yet).
So here's my suggestion. Further comments are directly in the source code (which compiles now without errors or warnings):
class SpecialList<T> {
static class CachedSupplier<R> implements Supplier<R> {
/* R is a priori an idependent parameter. If you call it T again, then you get a warning
* that the original T is hidden within the new nested class (though it wouldn't really matter here),
* as one doesn't need to refer to it. I made it static, as you don't need an instance to define it. */
private Supplier<? extends R> supplier;
private R value;
boolean isAvailable;
public CachedSupplier(Supplier<? extends R> supplier) {
this.supplier = supplier;
this.isAvailable = false;
}
#Override
public R get() {
if (!isAvailable) {
value = supplier.get();
isAvailable = true;
}
return value;
}
}
private Supplier<T> supplier;
private SpecialList(T value) {
/* in this case no lazy evaluation and no caching needed */
this.supplier = () -> value;
}
private SpecialList(Supplier<T> s) {
this.supplier = new CachedSupplier<T>(s);
}
private SpecialList(CachedSupplier<T> s) {
this.supplier = s;
}
private <R> SpecialList(Function<R, T> mapper, R v) {
/* This constructor is not needed anymore in my suggested code.
* I left it simply, in order to show how to fix the type errors related to it.
* if I understood correctly the intentions of this constructor,
* R and T have to be swapped w.r.t the original post.
* This is a constructor that uses an R-value and a mapper R->T to create a SpecialList<T> */
this.supplier = new CachedSupplier<T>(() -> mapper.apply(v));
}
public static <T> SpecialList<T> of(T value) {
return new SpecialList<>(value);
}
public static <T> SpecialList<T> of(Supplier<T> supplier) {
return new SpecialList<>(supplier);
}
public <R> SpecialList<R> map(Function<T, R> mapper) {
/* mapper is here different (opposite direction) than in the last of the constructors.
* Here we have an existing SpecialList containing a value of Type T which will be turned into a
* SpecialList containing a value of type R by applying a mapper T->R after getting the value from
* the original SpecialList */
return new SpecialList<R>(() -> mapper.apply(get()));
}
public T get() {
return this.supplier.get();
}
}
This solution doesn't yet take care of the toString() implementation. When you want to implement also the latter, it might be more natural to make SpecialList a subclass of CachedSupplier (with the additional map- and toString- methods and direct access to isAvailable)
Is there any way how to make class type parameter more narrow (add another bound to it) in concrete method?
Let's look at example
public class Value<T>
{
private final T value;
public Value(T value)
{
this.value = value;
}
public <V extends T> boolean eq(V value)
{
return Objects.equals(this.value, value);
}
// here, I want to create bound that T extends Comparable<T>
// error: type parameter cannot be followed by other bounds
public <V extends T & Comparable<T>> boolean gt(V value)
{
return ((V)this.value).compareTo(value) > 0;
}
// here, I want to create bound that T extends String
// error: interface expected here
public <V extends T & String> boolean match(V value)
{
return ((V)this.value).equalsIgnoreCase(value);
}
public static void main(final String[] args)
{
final Value<Integer> integerValue = new Value<>(10);
integerValue.eq(10); // should compile
integerValue.gt(5); // should compile
integerValue.match("hello"); // shouldn't compile because match operates only on String values
final Value<String> stringValue = new Value<>("Foo");
stringValue.eq("Foo"); // should compile
stringValue.gt("bar"); // should compile
stringValue.match("foo"); // should compile
}
}
In this example line
integerValue.match("hello");
doesn't compile, which is correct, but the class can't be compile too due to restriction that type parameter cannot be followed by other bounds
Is there any other way how to achieve this?
An instance method must be available to all instances of the class. You can't declare a method that only exists for some instances of the class, those with certain type parameters.
What you can do is make a generic static method that takes an instance of the class as an argument. Since the generic type parameter is now specific to the method, it can be restricted to what the method wants:
public static <T> boolean eq(Value<T> obj, T value)
{
return Objects.equals(obj.value, value);
}
public static <T extends Comparable<T>> boolean gt(Value<T> obj, T value)
{
return obj.value.compareTo(value) > 0;
}
public static <T extends String> boolean match(Value<T> obj, T value)
{
return obj.value.equalsIgnoreCase(value);
}
Using the & operator assumes that V directly extends T and String, so one of them has to be an interface as a class can not directly extend two other classes. It doesn't work either for Comparable<T> as compilator can not garantee type safety with that typo.
You just have to use a , :
// here, I want to create bound that T extends Comparable<T>
// error: type parameter cannot be followed by other bounds
public <V extends T, T extends Comparable<T>> boolean gt(V value)
{
return ((V)this.value).compareTo(value) > 0;
}
// here, I want to create bound that T extends String
// error: interface expected here
public <V extends T, T extends String> boolean match(V value)
{
return ((V)this.value).equalsIgnoreCase(value);
}
Type bounds aren't the solution you need. What you need are subclasses.
public class Value<T>
{
protected final T value;
public Value(T value)
{
this.value = value;
}
public boolean eq(T value)
{
return Objects.equals(this.value, value);
}
}
public class ComparableValue<T extends Comparable<T>> extends Value<T>
{
public ComparableValue(T value)
{
super(value);
}
public boolean gt(T value)
{
return this.value.compareTo(value) > 0;
}
}
public class StringValue extends ComparableValue<String>
{
public StringValue(String value)
{
super(value);
}
public boolean match(String value)
{
return this.value.equalsIgnoreCase(value);
}
}
Then main becomes
public static void main(final String[] args)
{
final ComparableValue<Integer> integerValue = new ComparableValue<>(10);
integerValue.eq(10); // will compile
integerValue.gt(5); // will compile
integerValue.match("hello"); // will not compile
final StringValue stringValue = new StringValue("Foo");
stringValue.eq("Foo"); // will compile
stringValue.gt("bar"); // will compile
stringValue.match("foo"); // will compile
}
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();
}
}
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?
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:
I am declaring a generic CompositeKeyBasedCreditCardDetailsHolder with type parameters K, V.
That type has a field called numberProviderPair
Which itself is a generic type CompositeKeyImplementer<K1, K2>
and in fact which itself can be any CompositeKeyImplementer<K1, K2> where the type parameters K1, K2 are a subtype (inclusive) of K
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.