I have an interface which has map-like functionality, but does not implement Java's Map interface.
The map interface also implements Iterable<Object>; it iterates over the keys of the map
I'd like to use this in the body of an enhanced loop (see below), but without an assertion, and use get to retrieve values for the iterated keys, and without an [ERROR] from the Checker Framework.
Is that at all possible and could you provide pointers where to start or examples to learn from? I tried haphazardly to sprinkle some #KeyFors here and there, but with a lack of fully understanding what I'm doing it could take a while before I hit the right spots ;-)
I understand we might use an "Entry Iterator" and avoid to have to solve this problem in the first place, but I'm really just interested in learning how to teach the Checker Framework about the semantic relation between a key iterator and an #Nullable get method.
Here's a minimal working example:
import org.checkerframework.checker.nullness.qual.Nullable;
interface IMap extends Iterable<Object> {
#Nullable Object get(Object o);
IMap put(Object key, Object value); // immutable put
IMap empty();
default IMap remove(Object key) {
IMap tmp = empty();
for (Object k : this) {
if (!k.equals(key)) {
tmp.put(k, get(k)); // get(k) is always non-null because of the key iterator
}
}
return tmp;
}
}
class Map implements IMap {
java.util.Map<Object, Object> contents = new java.util.HashMap<>();
public Map() { }
private Map(java.util.Map<Object, Object> contents) {
this.contents = contents;
}
#Override
public #Nullable Object get(Object key) {
return contents.get(key);
}
#Override
public IMap empty() {
return new Map();
}
#Override
public IMap put(Object key, Object value) {
java.util.Map<Object, Object> newContents = new java.util.HashMap<>();
newContents.putAll(contents);
newContents.put(key, value);
return new Map(newContents);
}
#Override
public java.util.Iterator<Object> iterator() {
return contents.keySet().iterator();
}
}
The Nullness Checker is warning you that the specifications (the type annotations) are inconsistent with the code itself.
Nullness problem
The key problem with your code is here:
tmp.put(k, get(k))
and the error message is:
error: [argument.type.incompatible] incompatible types in argument.
tmp.put(k, get(k)); // get(k) is always non-null because of the key iterator
^
found : #Initialized #Nullable Object
required: #Initialized #NonNull Object
Here are the two specifications that are incompatible:
put requires a non-null second argument (recall that #NonNull is the default):
public IMap put(Object key, Object value) { ... }
get might return null at any time, and clients have no way to know when the return value might be non-null:
#Nullable Object get(Object o);
If you want to state that the return value of a method is nullable in general, but is non-null in certain situations, then you need to use a conditional postcondition such as #EnsuresNonNullIf.
That said, the Nullness Checker has special handling for Map.get. Your code doesn't use that, because you don't have a method that overrides java.util.Map.get (though it does have a class named Map that has nothing to do with java.util.Map).
If you want special-case handling for IMap.get, then either:
your class should extend java.util.Map, or
you should extend the Nullness Checker to recognize your class.
Map key problem
could you provide pointers where to start or examples to learn from?
I suggest starting with the Checker Framework Manual. It has lots of explanations and examples. You should read at least the Map Key Checker chapter. It links to further documentation, such as Javadoc for #KeyFor.
I tried haphazardly to sprinkle some #KeyFors here and there, but with a lack of fully understanding what I'm doing it could take a while before I hit the right spots ;-)
Please don't do that! That way lies suffering. The manual tells you not to do that; instead, think first and write specifications that describe your code.
Here are three #KeyFor annotations that you culd write:
interface IMap extends Iterable<#KeyFor("this") Object> {
...
default IMap remove(#KeyFor("this") Object key) {
...
#SuppressWarnings("keyfor") // a key for `contents` is a key for this object
public java.util.Iterator<#KeyFor("this") Object> iterator() {
These annotations state, respectively:
The iterator returns keys for this object.
Clients must pass a key for this object.
The iterator returns keys for this object. I suppressed a warning because this object acts as a wrapper for a contained object, and I don't recall that the Checker Framework has a way to say, "This object is a wrapper around a field and each of its methods has the same properties as the methods of that field."
The result type-checks without problems (except the nullness one noted in the first section of this answer):
import org.checkerframework.checker.nullness.qual.KeyFor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
interface IMap extends Iterable<#KeyFor("this") Object> {
#Nullable Object get(Object o);
IMap put(Object key, Object value); // immutable put
IMap empty();
default IMap remove(#KeyFor("this") Object key) {
IMap tmp = empty();
for (Object k : this) {
if (!k.equals(key)) {
tmp.put(k, get(k)); // get(k) is always non-null because of the key iterator
}
}
return tmp;
}
}
class Map implements IMap {
java.util.Map<Object, Object> contents = new java.util.HashMap<>();
public Map() {}
private Map(java.util.Map<Object, Object> contents) {
this.contents = contents;
}
#Override
public #Nullable Object get(Object key) {
return contents.get(key);
}
#Override
public IMap empty() {
return new Map();
}
#Override
public IMap put(Object key, Object value) {
java.util.Map<Object, Object> newContents = new java.util.HashMap<>();
newContents.putAll(contents);
newContents.put(key, value);
return new Map(newContents);
}
#Override
#SuppressWarnings("keyfor") // a key for `contents` is a key for this object
public java.util.Iterator<#KeyFor("this") Object> iterator() {
return contents.keySet().iterator();
}
}
To summarize the informative accepted answer:
There is no way to annotate the given code example such that the semantic relation between the iterator and the get method of IMap can be specified to the Checker Framework;
As a result the current reported error requires an local non-nullness assert, a rewrite of the code which avoids a key iterator or a SuppressWarning annotation.
An extension to the checker framework would be necessary, among the lines of how it was special-cased for java.util.Map, if we want to avoid these workarounds.
Related
I have an interface called InterA which has a method call operate() that has its return value as Result.
public interface InterA<T>
{
Result operate(T source);
}
Assume Class MachineOperator and Class CarOperator are implementations of the above with two T object types.
public class MachineOperator implements InterA<Machine>
{
Result operate(Machine source)
{
//Do some operation here and return result.
}
}
public class CarOperator implements InterA<Car>
{
Result operate(Car source)
{
//Do some operation here and return result.
}
}
And I want to keep these implementations in a map so that when I give the class I can have the implementation. So my map would be like this.
Map<Class<?>, InterA<T>> map = new HashMap<>();
And when I go to fill the map I have to do following,
map.put(Machine.class, (InterA<T>) new MachineOperator());
map.put(Car.class, (InterA<T>) new CarOperator());
Is there any way of defining the map so there is no longer a need to cast when adding?
=============================================================
I have tried the following also which can avoid casting when adding to map.
public interface InterA
{
Result <T> operate(T source);
}
Then in each implementation I have to have additional casting for each T.
public class MachineOperator implements InterA
{
< Machine> Result operate(Machine source)
{
(Machine) source; //Need to cast for Machine In-order to read attributes.
//Do some operation here and return result.
}
}
Out of these what would be the correct approach? Is there any different solution?
You could write a wrapper for your Map to allow adding only corresponding InterA objects:
class ClassMap {
private Map<Class<?>, InterA<?>> map = new HashMap<>();
public <T> void put(Class<T> key, InterA<T> value) {
map.put(key, value);
}
//we suppress unchecked cast warning because for Class<T> key we can put only InterA<T> value
#SuppressWarnings("unchecked")
public <T> InterA<T> get(Class<T> key) {
return (InterA<T>) map.get(key);
}
}
then you can use it like this
ClassMap map = new ClassMap();
map.put(Machine.class, new MachineOperator());
InterA<Machine> operator = map.get(Machine.class);
You should replace T in Map declaration with some common ancestor for Car and Machine.
If there is no common class, just use Object or ?.
That will eliminate compilation errors, but will increase chances of runtime errors. You will have be sure that the instance of the parameter is what is expected.
The Lower Bounded Wildcards is what you are looking for.
Your map declaration should looks like that:
Map<Class<?>, ? super InterA<?>> map = new HashMap<>();
There is some interface called say called Foo.
interface Foo {
void add(Object key, Object value);
Object get(Object key);
void someOtherMethodUnessentialToTesting();
}
There is an implementation in the test called MockFoo that implements most methods in a "default" way (doing nothing, returning null, basically only implementing them so it compiles). However, it implements a couple to give real functionality, and they are methods that insert and read from a Map. (If the last bit wasn't essential I would just use a Mockito mock and wouldn't even be asking.)
// The current "bad mock"
class MockFoo implements Foo {
Map<Object, Object> map = new ...
#Override
void add(Object key, Object value) {
map.put(key, value);
}
#Override
Object get(Object key) {
map.get(key);
}
#Override
void someOtherMethodUnessentialToTesting()
{}
}
The problem is that because this is not a Mockito mock, every time the interface changes the test has to be updated. Yes, people should check better to fix all implementations of the interface they change, but there really shouldn't be an implementation in the test in the first place in my opinion.
I am confused on how to solve this. My instinct is to make it abstract and implement only those methods then mock it somehow so that it calls those "real" methods when it needs them. I read that Mockito has a thenDoRealMethod() for stubs but this is only for returning values so it would not work on a void method.
// My abstract class I was trying to stub somehow
abstract class FooForTest implements Foo {
Map<Object, Object> map = new ...
#Override
void add(Object key, Object value) {
map.put(key, value);
}
#Override
Object get(Object key) {
map.get(key);
}
}
I realize this may be a design issue and adding in an AbstractFoo in the real code is probably best (because the add and get won't change really) but I am more curious if there is a way to fix this once-and-for-all just by modifying the test code.
Using a technique like in this SO answer, you can use CALLS_REAL_METHODS as a default answer—or, as you suggested, you can use the default answer (RETURNS_DEFAULTS) and individually stub certain methods to call your fake. Because you want Mockito's behavior to show through, I'd recommend the latter.
There's an equivalent to thenCallRealMethod for void methods: doCallRealMethod. You need to start with do because when(T value) has no value to take [and subsequently ignore].
abstract class FooForTest implements Foo {
public static Foo create() {
FooForTest mockFoo = mock(FooForTest.class);
mockFoo.map = new HashMap<>();
when(mockFoo.get(any())).thenCallRealMethod();
doCallRealMethod().when(mockFoo).add(any(), any());
return mockFoo;
}
Map<Object, Object> map = new ...
#Override
void add(Object key, Object value) {
map.put(key, value);
}
#Override
Object get(Object key) {
map.get(key);
}
}
Without complicated reflection, I can't think of any way to automatically let Mockito call real methods for unimplemented methods only. If you were to write an Answer for that, though, you could use it for all methods by passing it into the call to mock. You will also need to initialize your field explicitly in your create method, as the mock is not a real object and will not be properly initialized.
Another alternative would be to have empty method stubs, and use spy() instead; this would solve some of the initialization concerns.
This technique is called partial mocking, and beware that it can act as a code smell—specifically, that the class under test is violating the Single Responsibility Principle. In any case you should be very careful when adding methods to widely-implemented interfaces, for all of these reasons, and consider AbstractFoo (or a fully-implemented FakeFoo) as sensible upgrades later.
In java, how should I create a Map<String,String> that has unmodifiable keys, while keeping the values modifiable.
I'd like to hand this Map<String,String> through an interface for someone else to add/change the Map values, but not be able to change the Map keys.
The background on higher level problem is that I have list/set of variable names (with tree like structure) (represented as java String) that I'd like code on the other side of the java interface to be able to populate aliases (also Strings) for each of the variable names. I'd like to have multiple implementations of this interface so naming tree hierarchy can be aliases different ways to fit different situations. Having the interface implementation populate a Map<String,String> with bunch of keys already set-in-stone (and maybe containing defaults for the values) and allowing it to modify the values (but not the keys), seems like the best approach. I'm creating a mapping between names and alias, so Map<> makes sense.
Back to the lower level problem. I'd like my code to resemble:
public class MyClass
{
public interface IMyMapper
{
void build(Map<String,String> mapping);
}
IMyMapper mapper;
// How I'd like to use it
void work()
{
Map<String,String> map ;
// Magic something like Collections unmodifiableMap, but only for keys
// Maybe my question should be how this magic for UnmodifiableMap works, so I could reproduce it??
mapper.build(map);
// Because Maps<> are by reference, changed they made (to the values) would be reflected here
}
}
public class TheirClass implements MyClass.IMyMapper
{
#Override
public void build(Map<String,String> mapping)
{
// use mapping like Map<String,String> without extra/foreign classes
// but not be able to modify the Map keys
// only be able to change the Map values
// Should be able to use all of the awesome Map stuff, like foreach, values, compute
}
}
I know there is Collections unmodifiableMap(Map<> m) but that also makes the values unmodifiable. If my values were mutable objects, then I could modify them but I'd like to stick with Strings (avoiding creating Class with set/get for single String member, or creating Structure-like-class with public String member).
AKA, I'd like to avoid creating my own mutable class-values, and use Collections unmodifiableMap() to make the keys and value references unmodifiable:
// mutable reference to a String
public class ExtraWorkForForEveryone
{
public String value;
public void setValue(String value) { ... }
public String getValue() { ... }
}
// and then use:
void work()
{
Map<String,ExtraWorkForEveryone> map;
map = Collections.unmodifiableMap( ... );
// because Collections.unmodifiableMap() only stops them from changing the Map references,
// the interfacer could still change the ExtraWorkForEveryone internals.
// so they could not change keys refs or value refs, but they could change value data.
mapper.build(map);
// Because Maps<> are by reference, changed they made (to the values) would be reflected here
}
I could extend or implement my own Map, then (like how Collections unmodifiableMap()) override all methods that could change the keys throw UnsupportedOperationException. But with Java 8, there has been a large number of methods added using Lambda functions, which would be nice for Interface implementers to have access to, as long as they could not change the keys.
AKA, I'd like to avoid this lengthy and error-prone technique:
public final class FinalHashMap extends HashMap
{
#Override // anything that might be able to change the Map Keys
so_many_methods_and_edge_cases()
{ throws UnsupportedOperationException }
}
Is there existing interface that only allows changing the data of values of Maps<>?
What are my other options for creating something resembling a Map<String,String> that has unmodifiable keys, but modifiable values? I am interested in good coding practices, if possible.
Seems like you're looking for the Proxy Pattern.
Detailed answer:
The idea is to use what's called a proxy to interact with the map. The proxy will intercept all calls to the map; you should only be able to interact with the map through the proxy. It acts as an interface between the client and the map.
A proxy is a skeleton of what you are "wrapping". Since you are creating a proxy for a map, the proxy should implement the Map interface:
class ImmutableMap<K, V> implements Map<K, V> {
private Map<K, V> map;
public ImmutableMap(Map<K, V> map) {
this.map = new HashMap<>(map); // detach reference
}
//implement methods from Map
}
Most methods will simply telescope to map. Modify the methods you need to prevent removing keys or adding new keys to the map, such as put, putAll and remove:
final class ImmutableMap<K, V> implementsMap<K, V> {
private Map<K, V> map;
public ImmutableMap(Map<K, V> map) {
this.map = new HashMap<>(map);
}
#Override
public int size() {
return map.size();
}
#Override
public boolean isEmpty() {
return map.isEmpty();
}
#Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
#Override
public V get(Object key) {
return map.get(key);
}
#Override
public V put(K key, V value) {
if(!map.containsKey(key)) {
throw new IllegalArgumentException("Cannot add new keys!");
}
return map.put(key, value);
}
#Override
public V remove(Object key) {
throw new UnsupportedOperationException("You cannot remove entries from this map!");
}
#Override
public void putAll(Map<? extends K, ? extends V> map) {
for(K key : map.keySet()) {
if(!this.map.containsKey(key)) {
throw new IllegalArgumentException("Cannot add new keys to this map!");
}
}
this.map.putAll(map);
}
#Override
public void clear() {
throw new UnsupportedOperationException("You cannot remove entries from this map!");
}
#Override
public Set<K> keySet() {
return Collections.unmodifiableSet(map.keySet());
}
#Override
public Collection<V> values() {
return Collections.unmodifiableSet(map.values()); //prevebt changing values to null
}
#Override
public Set<Map.Entry<K, V>> entrySet() {
//to allow modification of values, create your own ("immutable") entry set and return that
return Collections.unmodifiableSet(map.entrySet());
}
}
Keynotes:
Collections.unmodifiableSet should be used when returning sets from the map. This ensures that if a person attempts to modify a set returned from the map, it'll throw an UnsupportedOperationException
Creating a new Map containing the values of the map passed into the constructor prevents the client from modifying the ImmutableMap using the map they passed into it.
you may want to limit the size of your map
In over riding your put method you may use
if (map.size() == maxEntries) {
throw some exception;
I often see lists of objects in java holding beans whose objects are picked by inspecting an ID field, i.e.
List<BeanObj> list = …
BeanObj myObj = null;
for(BeanObj b : list)
if(b.getId().equals(whatIAmLookingFor)){
myObj = b;
break;
}
(The second variant of this is storing the objects in Hibernate and retrieve them by SQL.)
Using a Map interface would really be sensible here, but there are difficulties, i.e.
the key field may be changed (in general, or even concurrently)
the key may be non-trivial to reach (think of b.getRoot().getAttribute("id").equals(…)
Have there been approaches to address this in a more efficient way, like implementing a
SpecialMap<String, BeanObj>("id") // use String getId() on BeanObj
or even
SpecialMap<String, BeanObj>("getRoot().getAttribute({0})", "id")
// use String getAttribute("id") on result of getRoot()
with add() instead put() which makes use of the id getter function to build its internal map? Probably this map will require the mapped objects to implement some interface to allow the map being notified of updates on the id field.
Perhaps the map could also take care that changing the ID of an object to an ID of an existing object is either not possible or results in dropping the object that previously had that ID.
You can manage the functionnal aspect of adding element to your map by using guava utilities:
import com.google.common.base.Function;
public class SpecialMap<K, V> extends HashMap<K, V>{
private Function<V, K> function;
public SpecialMap(Function<V, K> function) {
this.function = function;
}
public void add(V value) {
K key = function.apply(value);
this.put(key, value);
}
public static void main(String[] args) {
SpecialMap<String, BeanObj> specialMap = new SpecialMap<String, BeanObj>(new Function<BeanObj, String>() {
#Override
public String apply(BeanObj arg) {
return arg.getRoot().getAttribute("id");
}
});
specialMap.add(new BeanObj());
}
}
In this example, the function will map your bean type to a string key.
I want to have a Map object to contain specific value types.
Map<String,Object> foo = new HashMap<String,Object>();
foo.put("1",new Integer(1));
foo.put("2", new String("hello"):
for (Map.Entry<Integer, Integer> entry : foo.entrySet()) {
if(entry.getValue() instanceof String) {
//do something
}
else if(entry.getValue() instanceof Double) {
//throw Exception
}
}
You can see what i am trying to accomplish. I need specific Value types in my Map object so i don't have to put lots of if/else statements. How do i accomplish this ?
If you don't like the idea of having multiple maps, you can use the nice OOP feature called overriding.
see Override Methods for more information.
I would probably use composition pattern to wrap the value types and then define a interface for the action depending of the value types.
public abstract class ValueType {
public abstract void valueSpecificAction();
}
public class DoubleValueType extends ValueType {
private Double value;
public DoubleValueType(Double value) {
this.value = value;
}
public void valueSpecificAction() {
//do double specific actions
}
}
public class StringValueType extends ValueType {
private String value;
public void valueSpecificAction() {
//do string specific actions
}
}
You can then add the objects of type ValueType in the maps.
Map<String,ValueType> foo = new HashMap<String,ValueType>();
foo.put("key1", new DoubleValueType(42.0));
later you can get the value, which has the type ValueType with the public method valueSpecificAction() which do the stuff depending on the specific type.
I don't see how you can avoid to have an if-else somewhere in general.
Jan's solution would spare you the if-else only if the value types can be determine at compulation-time. That is the case in the toy example above but is it so in your actual problem?
If it is a matter of making the code look cleaner you can always hide the if-else and conversions in a set of private methods.