Problem: We need to get a (String) key for different classes of objects.
For expendability we want to configure the Method to use to get the key String – instead of implementing many if-else with intanceOf…
Naive solution (with example data) is:
public static String getKey(Object object, Map<Class<?>, Method> keySources) {
Method source = keySources.get(object.getClass());
if (source == null) {
return null;
}
try {
return (String) source.invoke(object);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException("Error at 'invoke': " + e.getMessage(), e);
}
}
public static void main(String[] args) {
Map<Class<?>, Method> keySources = new HashMap<>();
try {
keySources.put(String.class, String.class.getMethod("toString"));
keySources.put(Thread.class, Thread.class.getMethod("getName"));
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException("Error at 'getMethod': " + e.getMessage(), e);
}
System.out.println(getKey("test", keySources));
System.out.println(getKey(new Thread("name"), keySources));
}
Desired solution would be like:
public static String getKey(Object object, Map<Class<?>, Function<Object, String>> keySources) {
Function<Object, String> source = keySources.get(object.getClass());
if (source == null) {
return null;
}
return source.apply(object);
}
public static void main(String[] args) {
Map<Class<?>, Function<Object, String>> keySources = new HashMap<>();
keySources.put(String.class, String::toString);
keySources.put(Thread.class, Thread::getName);
System.out.println(getKey("test", keySources));
System.out.println(getKey(new Thread("name"), keySources));
}
But String::toString is giving compilation error: The type String does not define toString(Object) that is applicable here
Constraints: We cannot modify the classes since they were generated.
I managed to get your code to pass compilation and run. I'm not sure why it works with lambda expressions but not with method references.
Perhaps there are better ways to do this.
public static <T> String getKey(T object, Map<Class<?>, Function<? extends Object, String>> keySources)
{
Function<T, String> source = (Function<T, String>) keySources.get(object.getClass());
if (source == null) {
return null;
}
return source.apply(object);
}
public static void main (java.lang.String[] args) throws Exception
{
Map<Class<?>, Function<? extends Object, String>> keySources = new HashMap<>();
keySources.put(String.class, s -> s.toString());
keySources.put(Thread.class, (Thread t) -> t.getName());
System.out.println(getKey("test", keySources));
System.out.println(getKey(new Thread("name"), keySources));
}
A Function<Object, String> is a function that accepts Object, in other words arbitrary objects as argument, so a function like String::toString, that requires its arguments to be String instances can’t fulfill the contract. That’s easy to fix, as you can use Object::toString instead, however, for Thread::getName, which requires the arguments to be Thread instances, there is no such replacement.
Since you are ensuring that the arguments are of the right type due to the map keys, you can solve this by converting each specific function to a Function<Object,String> that does a type cast:
public static <T,R> void put(Class<T> cl,
Function<T,R> f, Map<Class<?>,Function<Object,R>> map) {
map.put(cl, obj -> f.apply(cl.cast(obj)));
}
public static String getKey(Object object,
Map<Class<?>, Function<Object, String>> keySources) {
return keySources.getOrDefault(object.getClass(), x -> null).apply(object);
}
public static void main(String[] args) {
Map<Class<?>, Function<Object, String>> keySources = new HashMap<>();
put(String.class, String::toString, keySources);
// or put(String.class, Function.identity(), keySources);
put(Thread.class, Thread::getName, keySources);
System.out.println(getKey("test", keySources));
System.out.println(getKey(new Thread("name"), keySources));
}
Function take one parameter, which does not match the signature.
Try using Callable which take no parameter and return a value
Related
So here's a slightly tricky question (for me).
I have a generic object. Call it MyObject. This object has a method which returns something of the type T:
public class MyObject<T>
{
private T _t;
public MyObject(T t)
{
_t = t;
}
//...
public T get()
{
return _t;
}
}
(Obviously my "MyObject" does a bit more but that's the gist).
Now, I want to have a map of this type:
Map<String, MyObject<?>> m = new HashMap<>();
I want to be able to fetch maps using some predefined string name, and these maps can be of any MyObject. For example, I could call:
m.put("map_1", new MyObject<String>("String"));
m.put("map_2", new MyObject<Integer>(new Integer(3));
m.put("map_3", new MyObject<Long>(new Long(5));
etc.
But - and here's the tricky part - I want the map to "remember" the parameterized type of MyObject when I fetch some value from the map. Using
m.get("map_1");
would return a
MyObject<Object>
type, since the map was defined as containing
MyObject<?>
values. Thus:
m.get("map_1").get() // <-- This is an Object, not a String!
What modification (if any) is possible, in order to be able to get the correct - full - information regarding the MyObject fetched object, such that invoking the last line (m.get("map_1")) would return a
MyObject<String>
Thanks :)
Amir.
Typesafe Heterogeneous Containers from Joshua Bloch's Effective Java might work here. Basically you add a Class object to represent the type.
public class MyObject<T>
{
private T _t;
private Class<T> type;
public MyObject( Class<T> type, T t)
{
_t = t;
this.type = type;
}
//...
public T get()
{
return _t;
}
public Class<T> getType() { return type; }
}
Then you could do something like this:
public <T> T get( Map<String, MyObject<?>> map, String key, Class<T> type ) {
return type.cast( m.get( key ).get() );
}
Which is safe and will compile, but will throw a runtime error if you get the type wrong.
(Note I didn't actually compile that, so I might have syntax errors floating around. But most folks don't know how to use Class to cast objects.)
You can get the class.
Class c = m.get("map_1").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
Here is a full test.
public class GenericsTest {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Map<String, MyObject<?>> map = new HashMap<>();
MyObject<String> obj = new MyObject<>("hello");
map.put("greeting", obj);
Class c = map.get("greeting").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
}
static class MyObject<T> {
T t;
public MyObject(T t) {
this.t = t;
}
T get() {
return t;
}
}
}
The type system only knows about types, not objects, and therefore can not distinguish "key1" from "key2", because both are of type String.
If keys have different types, the easiest way is to encapsulate a weakly typed map, and use reflective casts to prove to the compiler the types are correct:
class Favorites {
private Map<Class<?>,?> map = new HashMap<>();
<V> V get(Class<V> clazz) {
return clazz.cast(map.get(clazz));
}
<V> void put(Class<V> clazz, V value) {
map.put(clazz, value);
}
}
Favorites favs = new Favorites();
favs.put(String.class, "hello");
favs.put(Integer.class, 42);
favs.get(String.class).charAt(1);
I am new to java 8 and trying to pass method names dynamically to get the values.
I have a Request request which has getInput1(), getInput2() methods. I can map the Optional statically like this:
void methodExecute(){
Optional<Request> request = Optional.of(new Request());
request.map(request::getInput1); //gives input1 value
request.map(request::getInput2); //gives input2 value
}
Can we do the same thing dynamically, if "getInput1" and "getInput2" method names are passed at runtime?
Below one is my approach. But it does not work.
#FunctionalInterface
public interface Function_WithExceptions<T, V,R, E extends Exception> {
R apply(T t,V v) throws E;
}
public class LambdaUtil<Input1, Input2> {
public static <Input1,Input2, R, E extends Exception>
Function_WithExceptions<Input1,Input2, R,E> rethrowFunction(Function_WithExceptions<Input1,Input2, R, E> function) throws E {
return (t,v) -> {
try {
return function.apply(t,v);
} catch (Exception exception) {
throwActualException(exception);
return null;
}
};
}
#SuppressWarnings("unchecked")
private static <E extends Exception> void throwActualException(Exception exception) throws E {
throw (E) exception;
}
}
public Function_WithExceptions getFunction(){
Function_WithExceptions<Request, String,Object,Exception> requestObjParamFun = (reqObj,req)->MethodUtils.invokeExactMethod(reqObj, req);
return requestObjParamFun;
}
If I understand you correctly, your problem can be solved like this:
static <T> Function<Request, T> reflect(String getterName, Class<T> resultType)
throws NoSuchMethodException, SecurityException {
Method method = Request.class.getMethod(getterName);
return req -> {
try {
return resultType.cast(method.invoke(req));
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
Here we just use normal reflection API to get the method of Request class by its name and return the function which calls it. Usage example:
// Some test Request class
static class Request {
public String getInput1() {return "aa";}
public Integer getInput2() {return 1;}
}
public static void main(String[] args) throws Exception {
Optional<Request> request = Optional.of(new Request());
System.out.println(request.map(reflect("getInput1", String.class))); // Optional[aa]
System.out.println(request.map(reflect("getInput2", Integer.class))); // Optional[1]
}
If you want to generate Functions for getters dynamically, but don’t want every invocation resorting to Reflection, you can use the same back-end as the Java language method references:
static <T> Function<Request, T> reflect(String getterName, Class<T> resultType)
throws ReflectiveOperationException {
MethodHandles.Lookup l=MethodHandles.lookup();
MethodType getter=MethodType.methodType(resultType);
MethodHandle target = l.findVirtual(Request.class, getterName, getter);
getter=target.type();
try {
return (Function)LambdaMetafactory.metafactory(l, "apply",
MethodType.methodType(Function.class),
getter.generic(), target, getter).getTarget().invokeExact();
} catch(Throwable ex) {
throw new ReflectiveOperationException(ex);
}
}
This can be used the same way as Tagir’s solution but won’t use Reflection on Function invocations (usually; as it’s JRE specific, it may use Reflection if method references also use Reflection in a particular JRE implementation).
But, as with the Reflection approach, it must be used carefully as an erroneous use is not spotted at compile time but at runtime.
I want to make a HashMap which contains HashSets as values and returns an empty HashSet when the key is not found.
public class IsbnHashMap<K,V> extends HashMap<K,V> {
protected V defaultValue;
public IsbnHashMap(V defaultValue) {
this.defaultValue = defaultValue;
}
#Override
public V get(Object k) {
return containsKey(k) ? super.get(k) : defaultValue;
}
}
However my implementation does not work.
private static IsbnHashMap<String, HashSet<String>> isbnToId = new IsbnHashMap<String, HashSet<String>>();
This returns "HashSet cannot be applied". If I try to change K,V in IsbnHashMap to <String, HashSet<String>> I get some funky errors as well. How can I implement this?
First it should be noted that in Java-8 you can use instead:
isbnToId.computeIfAbsent(isbn, k -> new HashSet<>()).add(_id);
Second, if you really want to do something like this in previous Java versions, you'd better to create separate method for this purpose (for example, getOrDefault()) in order not to violate the contract. Third, you need to create new HashSet<>() for every new key. If you return the same instance, it will be shared between given keys. If you don't expect users to modify it, it's better to use unmodifiable Collections.emptySet() as default value. This way users may safely do isbnToId.getOrDefault(isbn).contains(_id), but trying isbnToId.getOrDefault(isbn).add(_id) will result in exception. If you want to support the modification (prior to Java-8), you can, for example, pass the element class to the constructor instead:
public static class MyMap<K, V> extends HashMap<K, V> {
private Class<?> clazz;
public MyMap(Class<?> clazz) {
this.clazz = clazz;
}
public V getOrCompute(K key) {
V v = get(key);
if(v == null) {
try {
v = (V) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
put(key, v);
}
return v;
}
}
Usage example:
MyMap<String, Set<String>> map = new MyMap<>(HashSet.class);
map.getOrCompute("a").add("b");
map.getOrCompute("a").add("c");
map.getOrCompute("d").add("e");
System.out.println(map); // {a=[b, c], d=[e]}
Here we assume that instantiating the passed class with default constructor is ok. An alternative would be to pass the factory interface which is capable to produce the default values.
As Jon Skeet said ...
private static IsbnHashMap<String, HashSet<String>> isbnToId = new IsbnHashMap<String, HashSet<String>>(new HashSet<String>());
... however, that would return the same default object as Dunni pointed out.
So this will do:
private static HashMap<String, HashSet<String>> isbnToId = new HashMap<String, HashSet<String>>();
public static void coupleIsbnToId(String isbn, String _id) {
if (!isbnToId.containsKey(isbn)) {
isbnToId.put(isbn, new HashSet<String>());
}
isbnToId.get(isbn).add(_id);
}
Folks,
Is there any easy way to add generic class in non generic class.
Basically the cache manager will have map of Cache class which is implemented with proper generics.
But in below class we return (getCache method) Cache via get method it requires explicit cast at callers place how to avoid it.
e.g.
public class CacheManager {
private Map<String, Cache<?,?>> cacheMap = new HashMap<String, Cache<?,?>>();
public Cache<?,?> getCache(String cacheName) {
return cacheMap.get(cacheName);
}
public void addCache(String cacheName,Cache<?,?> cache) {
cacheMap.put(cacheName, cache);
}
}
Short answer: No (as far as I know).
The problem here is that what you are doing is not type-safe in Java at all. Have a look at this example:
import java.util.*;
class ClassCast {
public static void main(String[] args) {
HashMap<String, Pair<?, ?>> map = new HashMap<>();
map.put("test", new Pair<String, Integer>("Hello", 5));
Pair<Double, Double> pair = (Pair<Double, Double>) map.get("test");
}
}
class Pair<T,V> {
T a;
V b;
Pair(T a, V b) {
this.a = a;
this.b = b;
}
}
You would expect a ClassCastException here, but it compiles and runs perfectly fine. The reason for this is that the actual class of Pair<String, Integer> and Pair<Double, Double> is in fact just Pair (after type erasure).
To get type safety you have to implement the "Typesafe heterogeneous container pattern" (explained in detail in Effective Java by Josh Bloch). In short, you have to involve the type parameter in the key of your map. Depending on your needs, you might be able to use a class as key directly, otherwise you might have to make a key object.
Example implementation:
public class CacheManager {
private Map<MultiKey, Cache<?,?>> cacheMap = new HashMap<>();
#SuppressWarnings("unchecked")
public <T,V> Cache<T,V> get(String name, Class<T> t, Class<V> v) {
// Type-safe since types are encoded in key(i.e. map will not
// return something with the wrong type), and key is type-checked
// on insertion.
return (Cache<T,V>) cacheMap.get(new MultiKey(name, t, v));
}
public <T,V> void put(String name, Class<T> t, Class<V> v, Cache<T,V> cache) {
cacheMap.put(new MultiKey(name, t, v), cache);
}
class MultiKey {
Object[] keys;
Integer hash = null;
MultiKey(Object... keys) {
this.keys = keys;
}
#Override
public int hashCode() {
if (hash == null) hash = Arrays.hashCode(keys);
return hash;
}
#Override
public boolean equals(Object o) {
if (o == null || !(o instanceof MultiKey)) return false;
return Arrays.equals(keys, ((MultiKey) o).keys);
}
}
}
Example usage:
CacheManager mng = new CacheManager();
mng.addCache("SI", String.class, Integer.class, new Cache<String, Integer>());
Cache<String, Integer> cache = mng.getCache("SI", String.class, Integer.class);
System.out.println(cache);
It's not pretty, but it is actually type-safe. It can be improved depending on the actual situation though, so you should not use this code as is. For example, if you can get the types from the Cache object you don't need the Class arguments in addCache.
I came across an interesting problem and was wondering if and how could this be done in Java:
Create a method which can memoize any function/method . The method has the following arguments : the method/function and the argument(s) for it.
For example let's say i have this method :
int addOne(int a) { return a + 1;}
and i call my memoization method two times with the same arguments : addOne and 5 for example, the first call should actually call the addOne method and return the result and also store that result for that given argument. The second time when i call it should know this has been called before and just look up the previous answer.
My idea would be to have something like a HashMap<Callable,HashMap<List<Objects>,Object>> where you would store the previous answers and look them up later on.I think this can be somehow done with lambda expressions but i'm not that familiar with them.I'm not quite sure how to write this method and would appreciate some help.
Can this be done with this approach?
In Java 8 you can use ConcurrentHashMap.computeIfAbsent:
Map<Integer, Integer> cache = new ConcurrentHashMap<>();
Integer addOne(Integer x) {
return cache.computeIfAbsent(x -> x + 1);
}
DZone has a good tutorial which provides a solution that will work for any method:
The Memoizer class is quite simple:
public class Memoizer<T, U> {
private final Map<T, U> cache = new ConcurrentHashMap<>();
private Memoizer() {}
private Function<T, U> doMemoize(final Function<T, U> function) {
return input -> cache.computeIfAbsent(input, function::apply);
}
public static <T, U> Function<T, U> memoize(final Function<T, U> function) {
return new Memoizer<T, U>().doMemoize(function);
}
}
Using this class is also extremely simple:
Integer longCalculation(Integer x) {
try {
Thread.sleep(1_000);
} catch (InterruptedException ignored) {
}
return x * 2;
}
Function<Integer, Integer> f = this::longCalculation;
Function<Integer, Integer> g = Memoizer.memoize(f);
public void automaticMemoizationExample() {
long startTime = System.currentTimeMillis();
Integer result1 = g.apply(1);
long time1 = System.currentTimeMillis() - startTime;
startTime = System.currentTimeMillis();
Integer result2 = g.apply(1);
long time2 = System.currentTimeMillis() - startTime;
System.out.println(result1);
System.out.println(result2);
System.out.println(time1);
System.out.println(time2);
}
Running the automaticMemoizationExample method will produce the following result:
2
2
1000
0
You can memoize any function with Java 8's MethodHandles and lambdas if you're willing to give up type safety on the parameters:
public interface MemoizedFunction<V> {
V call(Object... args);
}
private static class ArgList {
public Object[] args;
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ArgList)) {
return false;
}
ArgList argList = (ArgList) o;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
return Arrays.equals(args, argList.args);
}
#Override
public int hashCode() {
return args != null ? Arrays.hashCode(args) : 0;
}
}
public static <V> MemoizedFunction<V> memoizeFunction(Class<? super V> returnType, Method method) throws
IllegalAccessException {
final Map<ArgList, V> memoizedCalls = new HashMap<>();
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle methodHandle = lookup.unreflect(method)
.asSpreader(Object[].class, method.getParameterCount());
return args -> {
ArgList argList = new ArgList();
argList.args = args;
return memoizedCalls.computeIfAbsent(argList, argList2 -> {
try {
//noinspection unchecked
return (V) methodHandle.invoke(args);
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
});
};
}
Working Example
This creates a variable-arity lambda that encloses the function and is almost as fast as calling the function directly (i.e., no reflection happens inside of call(Object...args)) after the lambda is constructed since we're using MethodHandle.invoke() instead of Method.invoke().
You can still do this without lambdas (replace with anonymous classes) and MethodHandles (replace with Method.invoke), but there will be performance penalties that make this less attractive for performance-conscious code.