Adding values to HashMap without casting - java

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<>();

Related

Java design pattern to avoid duplication

I have the following classes
public class MyCustomFactory extends SomeOther3rdPartyFactory {
// Return our custom behaviour for the 'string' type
#Override
public StringType stringType() {
return new MyCustomStringType();
}
// Return our custom behaviour for the 'int' type
#Override
public IntType intType() {
return new MyCustomIntType();
}
// same for boolean, array, object etc
}
Now, for example, the custom type classes:
public class MyCustomStringType extends StringType {
#Override
public void enrichWithProperty(final SomePropertyObject prop) {
super.enrichWithProperty(prop);
if (prop.getSomeAttribute("attribute01")) {
this.doSomething();
this.doSomethingElse();
}
if (prop.getSomeAttribute("attribute02")) {
this.doSomethingYetAgain();
}
// other properties and actions
}
}
But each custom type class like the string one above might have exactly the same if (prop.getSomeAttribute("blah")) { // same thing; }
Suppose I was to add another attribute, is there a nice way I can avoid having to duplicate if statements in each custom type class that needs it? I can move each if statement to utility class but I still need to add the call to the method in the utility class. I think we can do better.
You can create Map<String, Consumer<MyCustomStringType>>, where the key is your attribute name and value is the method call.
public class MyCustomStringType extends StringType {
private final Map<String, Cosnumer<MyCustomStringType>> map = new HashMap<>();
{
map.put("attribute01", o -> {o.doSomething(); o.doSomethingElse();});
map.put("attribute02", MyCustomStringType::doSomethingYetAgain);
// other properties and actions
}
#Override
public void enrichWithProperty(final SomePropertyObject prop) {
super.enrichWithProperty(prop);
map.entrySet().stream()
.filter(entry -> prop.getSomeAttribute(entry.getKey()))
.forEach(entry -> entry.getValue().accept(MyCustomStringType.this));
}
}
Depending on how you initialise this class (and whether this map is always the same), you might be able to turn in into static final immutable map.
I would also recommend naming it better, but a lot here depends on your domain and what this map and loop actually do.

How to create a Java Map<String,String> with unmodifiable keys?

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;

Map from property of entry to entry

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.

Can I have a wildcard in map like this Map<Class<?>, ?>

I have a private instance
private final Map<Class<?>, ?> map;
Syntactically, this is correct. What I want to do is this.
public class User {
}
public class UserSubclass extends User {
}
public class Role {
}
map.put(User.class, new User()); // valid
map.put(User.class, new UserSubclass()); // valid
map.put(Role.class, new Role()); // valid
// But when I do the following I need to get an error
map.put(User.class, new Role(); // invalid, compiler error
How should I declare the Map?
How can I instantiate an object of HashMap to this Map?
No, a simple java.util.Map does not support this. It is statically typed, and what you ask for is basically dynamic typing of one parameter based on the runtime-type of another one.
However, the Guava class ClassToInstanceMap implements exactly what you want:
A map, each entry of which maps a Java raw type to an instance of that type. In addition to implementing Map, the additional type-safe operations putInstance(java.lang.Class<T>, T) and getInstance(java.lang.Class<T>) are available.
You cannot do this by default, but what you can do, is to create your own Generic Safe Map, which will work.
The GenericMap would look like this:
class GenericMap extends HashMap<Class<?>, Object> {
public <T> T putClassAndInstance(Class<T> c, T o){
return c.cast(super.put(c, o));
}
public <T> T getInstance(Class<T> c) {
return c.cast(super.get(c));
}
}
And can then be used like:
GenericMap map = new GenericMap();
map.putClassAndInstance(User.class, new User()); // valid
map.putClassAndInstance(User.class, new UserSubclass()); // valid
map.putClassAndInstance(Role.class, new Role()); // valid
map.putClassAndInstance(User.class, new Role()); // invalid, compiler error
This way, you don't have to create special methods for the User and Role, and still have the safety of not adding the wrong object for the wrong type.

Map with custom value object

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.

Categories

Resources