Ответ от Lino указывает на пару fl aws в коде, но не работает, если не использовать повторно ту же лямбду.
Это потому, что o.getClass()
возвращает не класс того, что возвращает лямбда, а класс самой лямбды. Таким образом, приведенный ниже код возвращает два разных класса:
Util.memoize(() -> longCalculation(1));
Util.memoize(() -> longCalculation(1));
Я не думаю, что есть хороший способ узнать класс возвращаемого типа без фактического выполнения потенциально длительного кода, который из конечно - это то, чего вы хотите избежать.
Имея это в виду, я бы предложил передать класс в качестве второго параметра в memoize()
. Это даст вам:
@SuppressWarnings("unchecked")
public static <T> T memoize(Operator<T> o, Class<T> clazz) {
return (T) cache.computeIfAbsent(clazz, k -> o.op());
}
Это основано на том, что вы измените тип cache
на:
private static final Map<Class<?>, Object> cache = new ConcurrentHashMap<>();
К сожалению, вам нужно уменьшить Object
до T
, но вы можете гарантировать безопасность с аннотацией @SuppressWarnings("unchecked")
. В конце концов, вы контролируете код и знаете, что класс значения будет таким же, как и ключ на карте.
Альтернативой было бы использование Guavas ClassToInstanceMap
:
private static final ClassToInstanceMap<Object> cache = MutableClassToInstanceMap.create(new ConcurrentHashMap<>());
Это, однако, не позволяет вам использовать computeIfAbsent()
без приведения, так как он возвращает Object
, поэтому код станет немного более подробным:
public static <T> T memoize(Operator<T> o, Class<T> clazz) {
T cachedCalculation = cache.getInstance(clazz);
if (cachedCalculation != null) {
return cachedCalculation;
}
T calculation = o.op();
cache.put(clazz, calculation);
return calculation;
}
В качестве последнего примечания, вам не нужно указывать свой собственный функциональный интерфейс, но вы можете использовать интерфейс Supplier
:
@SuppressWarnings("unchecked")
public static <T> T memoize(Supplier<T> o, Class<T> clazz) {
return (T) cache.computeIfAbsent(clazz, k -> o.get());
}