I'm using a Hashmap as my in-memory cache. So basically, this is what I have:
private static final Map<String, Object> lookup = new HashMap<String, Object>();
public static Object get(CacheHelper key) {
return lookup.get(key.getId());
}
public static void store(CacheHelper key, Object value) {
lookup.put(key.getId(), value);
}
That's fine. But for every Object I "get" from the Map, I have to cast, which is very ugly.
I want to put ArrayList and many other different things into it.
Does anybody know an other solution to be typesafe ?
(For sure, I can create for every type a getter and setter, but is that the only solution ?
So, is there a better way to create an in-memory cache or does somebody have an idea how to wrap the hashmap to be more safe ?
One solution to this problem is to make your CacheHelper type generic, with CacheHelper<T>. Then create a wrapper for your map:
class MyCache {
private final Map<CacheHelper<?>, Object> backingMap = new HashMap<>();
public <T> void put(CacheHelper<T> key, T value) {
backingMap.put(key, value);
}
#SuppressWarnings("unchecked")
// as long as all entries are put in via put, the cast is safe
public <T> T get(CacheHelper<T> key) {
return (T) backingMap.get(key);
}
}
The Java compiler actually uses this approach internally; see e.g. here. You don't have to pass around explicit Class objects, but you do have to know what type is actually associated with each key, which is as it should be in well-behaved applications.
You could store the type of the elements ass well, pass the type you expect to the get() method and check there. AFAIK there's no built-in way to make storing different types typesafe without some common superclass or interface.
Basically you'd do this:
private static final Map<String, Object> lookup = new HashMap<>();
private static final Map<String, Class<?>> types= new HashMap<>();
public static <T> T get(CacheHelper key, Class<T> expectedType ) {
Class<?> type = types.get(key.getId());
if( type == null || expectedType == null || expectedType.isAssignableFrom( type ) ) {
throw new IllegalArgumentException("wrong type");
}
return (T)lookup.get(key.getId());
}
//if null values should be allowed, you'd need to change the signature to use generics
//and pass the expected type as well, e.g. <T> void store(CacheHelper key, T value, Class<T> type)
public static void store(CacheHelper key, Object value ) {
lookup.put(key.getId(), value);
types.put( key.getId(), value.getClass() );
}
Note that this still wouldn't catch any compile time errors since you're basically disabling generics inside your cache. Without a common superclass/interface or a generic type on cache itself there's no compile time way to catch those errors - at least no easy one.
If you just don't want to do the casts yourself you can hide them inside get() and live with the possible class cast exception. In that case you can let the compiler infer the type of T from the call (by assignment, explicitly set or type of the parameter).
However, passing the expected class easily provides additional information that you can use to create better error messages etc. which you'd not get from a ClassCastException.
You can pass a Class object as a parameter and use the Class.cast method :
public static <T> T get(CacheHelper key, Class<T> clazz){
return clazz.cast(lookup.get(key.getId());
}
You can use generics feature if java version is 7 (or upper)
public static <T> T get(CacheHelper key){
return (T)(lookup.get(key.getId());
}
And you can then call it as follows :
Myclass.<String>get(key);
In the above example I supposed the Myclass be a class containing the get() method
The get needs a Class<T>, as because of type erasure a cast (T) is a senseless no-op.
Either incorporate the Class all the way; IDs per class:
private final Map<Class<?>, Map<String, Object>> lookup = new HashMap<>();
public <T> T get(Class<T> klass, String id) {
Map<String, Object> mapById = lookup.get(klass);
if (mapById == null) {
return null;
}
Object value = mapById.get(id);
return klass.cast(value);
}
public <T> void store(Class<T> klass, String id, T value) {
Map<String, Object> mapById = lookup.get(klass);
if (mapById == null) {
mapById = new HashMap<>();
lookup.put(klass, mapById);
}
mapById.put(id, value);
}
The store not using value.getClass() to allow a get by interface or base class:
store(Number.class, "x", 3.14);
store(Number.class, "x", 3);
store(Number.class, "x", new BigDecimal("3.14"));
Or do only
public static <T> T get(Class<T> klass, String id) {
Object value = lookup.get(id);
return klass.cast(value);
}
(I simplified the original code a bit, for the readers.)
Related
is there a way to use a class, with generic types, without setting the maximum number?
I have this class
public class Repository<V> {
private Map<String, HashSet<V>> repo = new HashMap<>();
private static Repository instance = null;
private Repository() {}
public static synchronized Repository getInstance() {
if(instance == null) {
instance = new Repository();
}
return instance;
}
public void addRepository(String key) throws ClassNotFoundException, IOException {
repo.put(key, new HashSet<>());
}
.....
}
this is a "general repository", the HashMap contains an identifier as a key while as a value have HashSet<V> with the data.
I would like each HashSet in the HashMap to contain different class types. More precisely, I would like the generic type V to be different for each HashSet within the HashMap
how can i fix the code to be able to achieve this result?
You can't add a class parameter such as Repository<V> and expect V to be different for each type of entry in the map.
However, you may do something like this:
Remove the generic type from Repository:
public class Repository {
}
Generify the repository map so that it takes a Class<?> as key (instead of a String) and a Set<?> as value):
private final Map<Class<?>, Set<?>> repo = new HashMap<>();
Then, create one method to add a new repository and a method to get an existing repository as such:
public <T> void addRepository(Class<T> key) {
Set<?> existing = repo.putIfAbsent(key, new HashSet<>());
if (existing != null) {
throw new IllegalArgumentException("Key " + key + " is already associated to a repository");
}
}
public <T> Set<T> getRepository(Class<T> key) {
Set<?> subRepo = repo.get(key);
if (subRepo == null) {
throw new IllegalArgumentException("No repository found for key " + key);
}
return (Set<T>) subRepo; //unchecked cast
}
Note: the getRepository() will perform an unchecked cast, but it is a "safe" unchecked cast since the only way to add a new entry into your map is passing through <T> void addRepository(Class<T> key) and you won't be able to insert values that are not T inside the returned Set<T>.
Sample usage:
Repository repository = Repository.getInstance();
repository.addRepository(String.class);
repository.addRepository(Integer.class);
Set<String> stringRepo = repository.getRepository(String.class);
stringRepo.add("Hey");
stringRepo.add("Jude");
Set<Integer> intRepo = repository.getRepository(Integer.class);
intRepo.add(1);
intRepo.add(4);
However, I think you should have one repository per type, it would be cleaner because with the above solution, you're basically not leveraging at all on Java generics (except for the method <T> used in the getRepository method, for which you need to perform an unchecked cast anyway).
There's no way to achieve that cleanly. You can create a repository for each type you have, but you can not unite them into one repository with this setup.
At the moment I have an ArrayDeque of a Tuple as follows (I thought having a class in this might help), which is currently serialized and sent over a network:
private ArrayDeque<Triple<Integer, Object, Class>> paramList = new ArrayDeque<>();
The object types in the Tuple can be primitive such as ints, bool or real classes such as Date, time (none of which are my implementations).
Right now I'm iterating through the list and calling the method depending on what class type it is, obviously this becomes tedious modify and update when.
For example (st is PreparedStatement):
for (Triple<Integer, Object, Class> param : paramList) {
if(param.getMiddle() instanceof String){
st.setString(parm.getLeft(),param.getMiddle());
}else if(param.getMiddle() instanceof Integer){
st.setInt(parm.getLeft(),param.getMiddle());
} //... more class type checks doing the same thing
}
I came across this https://stackoverflow.com/a/5579385/5858208 post but as it's not classes I've implemented I can't really do much.
So my question is, is there a more efficient and maintainable way of implementing this idea since I have exactly what class-type it is in the tuple object?
You could create a Map that contains as keys all the classes that your code supports and as values lambda expressions that call the correct setter.
Something along these lines:
#FunctionalInterface
interface PSConsumer<T,U> {
void accept(T t, U u) throws SQLException;
}
private Map<Class<?>, PSConsumer<PreparedStatement, Triple<Integer, Object, Class<?>>>> setters = new HashMap<>();
{
setters.put(String.class, (st, param) -> st.setString(param.getLeft(), (String) param.getMiddle()));
setters.put(Integer.class, (st, param) -> st.setInt(param.getLeft(), (Integer) param.getMiddle()));
// add here code for all the classes that you support
}
for (Triple<Integer, Object, Class<?>> param : paramList) {
// this will throw an NPE if param.getMiddle() returns null or it's class is not in the map:
setters.get(param.getMiddle().getClass()).accept(st, param);
}
To extract values in a similar way you could use definitions likes these:
#FunctionalInterface
interface RSExtractor<T,U,R> {
R apply(T t, U u) throws SQLException;
}
private Map<Class<?>, RSExtractor<ResultSet, Triple<Integer, Object, Class<?>>, Object>> getters = new HashMap<>();
{
getters.put(String.class, (rs, param) -> rs.getString(param.getLeft()));
getters.put(Integer.class, (rs, param) -> rs.getInt(param.getLeft()));
}
public List<Object> get(ResultSet rs) throws SQLException {
List<Object> results = new ArrayList<>();
for (Triple<Integer, Object, Class<?>> param : paramList) {
results.add(getters.get(param.getRight()).apply(rs, param));
}
return results;
}
Please note that the interface RSExtractor looks much like the interface java.util.function.BiFunction, except that this interface allows the implementing method to throw an SQLException
I am trying to create an object holder util class to be short.
Forexample;
public ResponseAbc bringMeStuff(RequestAbc request){
ResponseAbc response = new ResponseAbc();
/* Setting request here.. */
response = bringMeLotsOfStuff(request);
/* Here I am calling the Util class */
Util.putToObjectHolder("myAbcResponse", response);
return response;
}
public void testMe(){
/* Testing objectHolder */
ResponseAbc newResponse = (ResponseAbc) Util.getFromObjectHolder("response");
}
Here is the Util class
public class Util<T> {
private static Util<?> instance = null;
private Map<String, T> objHolder;
private Util() {
}
/* I strongly think Util class should be singleton if I want to hold the map globally */
public static Util<?> getInstance() {
if (instance == null) {
instance = new Util();
}
return instance;
}
public static <T> void putToObjectHolder(String objectName, T objectType) {
// Map<String, T> holder = (Map<String, T>) getInstance().getObjHolder();
// holder.put(objectName, objectType);
getInstance().getObjHolder().put(objectName, objectType); //-> Argument error
}
public static <T> Object getFromObjectHolder(final String objectName) {
Map<String, T> holder = (Map<String, T>) getInstance().getObjHolder();
T obj = null;
for (Entry<String, T> entry : holder.entrySet()) {
if (entry.getKey().equals(objectName)) {
obj = entry.getValue();
} else {
obj = null;
}
}
return obj;
}
public Map<String, T> getObjHolder() {
if (objHolder == null) {
objHolder = new HashMap<String, T>();
}
return objHolder;
}
public void setObjHolder(Map<String, T> objHolder) {
this.objHolder = objHolder;
}
}
If I uncomment putToObjectHolder method, it works but I am not pretty sure it supposed to work that way. I mean creating an other map and assigning to it should do the trick.
What I intent to do is holding a static Map holder with single instance so I can put whatever object I want with a name and get that object whenever I want if it exist in that 'global holder'.
PS: It is pretty messy with type safety warnings for sure, I would love to improve that aswell though I am not sure how to.
Thanks in advance.
Putting aside the singleton part, are you trying to use generics to get objects (of varying types) into and out of the same Map whilst retaining type safety? That is, if you put into the map (for a given key) say a String then getting this value out will only compile if it is assigned to (or used as) a string. (And, also, there are no casts in the code.)
This can be done but it is a bit involved as you need to define keys that have the type of the corresponding value.
See: Java map with values limited by key's type parameter
Joshua Block also had a good article on this somewhere but I don't seem to be able to find it.
This seems to be what you are trying to achieve with your put method. You won't be able to do it with strings as keys though - you'll need a genericized, typed key.
You are not using generic the way they are meant to be used. Take a look at the ArrayList class to learn the true potential of generics.
Also singleton of this class serves no purpose as you only need a "singleton" of a HashMap.
Maybe I do not see what you are trying to accomplish but this is essentially what you are trying to do. Why don't you just use a HashMap and be done with it?
import java.util.HashMap;
public class Util {
private static HashMap<String, Object> values = new HashMap<String, Object>;
private Util() {
}
public static void put(String key, Object value) {
values.put(key, value);
}
public static Object get(String key) {
return values.get(key);
}
public static void main(String[] args) {
String s = "This is a test.";
Util.put("test", s);
System.out.println(Util.get("test"));
System.out.println(Util.get("another test"));
}
}
I want to create a map that will provide the benefits of generics, whilst supporting multiple different types of values. I consider the following to be the two key advantages of generic collections:
compile time warnings on putting wrong things into the collection
no need to cast when getting things out of collections
So what I want is a map:
which supports multiple value objects,
checks values put into the map (preferably at compile-time)
knows what object values are when getting from the map.
The base case, using generics, is:
Map<MyKey, Object> map = new HashMap<MyKey, Object>();
// No type checking on put();
map.put(MyKey.A, "A");
map.put(MyKey.B, 10);
// Need to cast from get();
Object a = map.get(MyKey.A);
String aStr = (String) map.get(MyKey.A);
I've found a way to resolve the second issue, by creating an AbstractKey, which is generified by the class of values associated with this key:
public interface AbstractKey<K> {
}
public enum StringKey implements AbstractKey<String>{
A,B;
}
public enum IntegerKey implements AbstractKey<Integer>{
C,D;
}
I can then create a TypedMap, and override the put() and get() methods:
public class TypedMap extends HashMap<AbstractKey, Object> {
public <K> K put(AbstractKey<K> key, K value) {
return (K) super.put(key, value);
}
public <K> K get(AbstractKey<K> key){
return (K) super.get(key);
}
}
This allows the following:
TypedMap map = new TypedMap();
map.put(StringKey.A, "A");
String a = map.get(StringKey.A);
However, I don't get any compile errors if I put in the wrong value for the key. Instead, I get a runtime ClassCastException on get().
map.put(StringKey.A, 10); // why doesn't this cause a compile error?
String a = map.get(StringKey.A); // throws a ClassCastException
It would be ideal if this .put() could give a compile error.
As a current second best, I can get the runtime ClassCastException to be thrown in the put() method.
// adding this method to the AbstractKey interface:
public Class getValueClass();
// for example, in the StringKey enum implementation:
public Class getValueClass(){
return String.class;
}
// and override the put() method in TypedMap:
public <K> K put(AbstractKey<K> key, K value){
Object v = key.getValueClass().cast(value);
return (K) super.put(key, v);
}
Now, the ClassCastException is thrown when put into the map, as follows. This is preferable, as it allows easier/faster debugging to identify where an incorrect key/value combination has been put into the TypedMap.
map.put(StringKey.A, 10); // now throws a ClassCastException
So, I'd like to know:
Why doesn't map.put(StringKey.A, 10) cause a compile error?
How could I adapt this design to get meaningful compile errors on put, where the value is not of the associated generic type of the key?
Is this is a suitable design to achieve what I want (see top)? (Any other thoughts/comments/warnings would also be appreciated...)
Are there alternative designs that I could use to achieve what I want?
EDIT - clarifications:
If you think this is a bad design - can you explain why?
I've used String and Integer as example value types - in reality I have a multitude of different Key / value type pairs that I would like to be able to use. I want to use these in a single map - that's the objective.
You are messing with generics and overloading in a bad way. You are extending HashMap<AbstractKey, Object> and so your class is inheriting the method Object put(AbstractKey k, Object v). In your class you are defining another put method with a different signature, which means you are just overloading the put method, instead of overriding it.
When you write map.put(StringKey.A, 10), the compiler tries to find a method that conforms to the argument types put(StringKey, Integer). Your method's signature doesn't apply, but the inherited put's does -- StringKey is compatible with AbstractKey and Integer is compatible with Object. So it compiles that code as a call to HashMap.put.
A way to fix this: rename put to some custom name, like typedPut.
BTW talking from experience your approach is very fun and engaging, but in real life it just isn't worth the trouble.
Item 29: Consider typesafe heterogeneous containers.—Joshua Bloch, Effective Java, Second Edition, Chapter 5: Generics.
IMHO, every problem comes from the original design smell: wanting to put values of different types into the map. I would wrap your Integer and String values into a common Value type instead. Something like this:
public class Value {
private enum Type {
STRING, INTEGER;
}
private Type type;
private Object value;
private Value(Object value, Type type) {
this.value = value;
this.type = type;
}
public static Value fromString(String s) {
return new Value(s, Type.STRING);
}
public static Value fromInteger(Integer i) {
return new Value(i, Type.INTEGER);
}
public Type getType() {
return this.type;
}
public String getStringValue() {
return (String) value;
}
public Integer getIntegerValue() {
return (Integer) value;
}
// equals, hashCode
}
This way, you just need a Map<SomeKey, Value>, and you can safely get the value from the map:
Value v = map.get(someKey);
if (v.getType() == Type.STRING) {
String s = v.getStringValue();
}
else if (v.getType() == Type.INTEGER) {
Integer i = v.getIntegerValue();
}
Consider this: (Thanks to Effective java)
public class TypedMap {
private Map<AbstractKey<?>, Object> map = new HashMap<AbstractKey<?>, Object>();
public <T> T get(AbstractKey<T> key) {
return key.getType().cast(map.get(key));
}
public <T> T put(AbstractKey<T> key, T value) {
return key.getType().cast(map.put(key, key.getType().cast(value)));
}
public static interface AbstractKey<K> {
Class<K> getType();
}
public static enum StringKey implements AbstractKey<String> {
A, B;
public Class<String> getType() {
return String.class;
}
}
public static enum IntegerKey implements AbstractKey<Integer> {
C, D;
public Class<Integer> getType() {
return Integer.class;
}
}
}
This generate compile time error
TypedMap map = new TypedMap();
TypedMap.AbstractKey<Integer> intKey = TypedMap.IntegerKey.C;
TypedMap.AbstractKey<String> strKey = TypedMap.StringKey.A;
map.put(strKey, "A");
map.put(intKey, 10);
map.put(strKey, 10); // this cause a compile error?
I want to create a mapping from (a) class type to (b) long (the identifier of the object of the defined class type) to (c) the object itself.
I have the following:
protected HashMap<Class<?>, HashMap<Long, ?>> obj = new HashMap<Class<?>, HashMap<Long, ?>>();
Is it possible to somehow denote that the first ? must be of the same type than the second ?? I would expect something like this, but this is ofcourse not possible:
protected <T> HashMap<Class<T>, HashMap<Long, T>> obj = new HashMap<Class<T>, HashMap<Long, T>>();
As an alternative, you could use a small amount of not-type-safe code encapsulated in a way that enforces your constraint:
class Cache {
private Map<Class<?>, Map<Long, ?>> items = new HashMap<Class<?>, Map<Long, ?>>();
private <T> Map<Long, T> getItems(Class<T> type) {
#SuppressWarnings("unchecked")
Map<Long, T> result = (Map<Long, T>) items.get(type);
if (result == null) {
result = new HashMap<Long, T>();
items.put(type, result);
}
return (Map<Long, T>) result;
}
public <T> void addItem(Class<T> type, Long id, T item) {
getItems(type).put(id, item);
}
public <T> T getItem(Class<T> type, Long id) {
return type.cast(getItems(type).get(id));
}
}
The type.cast() in getItem() isn't necessary for the compiler to not complain, but it would help catch an object of the wrong type getting into the cache early.
Each occurence of a wildcard corresponds to a different type, and the only appropriate scope for a type parameter representing the type is the entry in the outer HashMap. Unfortunately, HashMap does not allow constraining the entry type in its type parameter like:
class Entry<K,V> {
// fields omitted
}
class Map<E extends Entry<?,?> {
}
class EntityCacheEntry<E> extends Entry<Class<E>, Map<Entry<Long, E>>> { }
class EntityCache extends Map<EntityCacheEntry<?>> { }
Even if it did, there is no way to implement Map.get without using unchecked casts, because we'd have to constrain its type parameter to a particular member of the type family represented by E - and you can't constrain a type parameter of a type parameter in Java.
Therefore, your only recourse is writing a facade whose api enforces the type invariant, but internally uses casts:
class EntityCache {
Map<Class<?>, Map<Long, Object>> map = new HashMap<>();
public <E> void put(Class<E> clazz, long id, E entity) {
map.get(clazz).put(id, entity);
// TODO create map if it doesn't exist yet
}
public <E> E get(Class<E> clazz, long id) {
return clazz.cast(map.get(clazz).get(id));
// TODO what if not found?
}
}
You could extend the HashMap class with your specific generic definitions and make a generic class that takes the <T> as argument, something like this:
public class MyHashMap<T> extends HashMap<Class<T>, HashMap<Long, T>> { ...
What you probably want is: HashMap<Class<?>, HashMap<Long, Object>>. Because you will be putting objects of different types in it, Object is the type parameter that will allow you to add any type of object.
Don't get confused with the wildcard (?). It has the opposite meaning -- it means that the parameter is some type (thus all the objects must be of that type) but we don't know what it is, thus we can't put anything in it at all. That is not what you want.