为什么不允许定义这样的静态成员:
private static final <T extends Object> Map<Class<T>, BiFunction<T, T, Boolean>> SPECIFIC_HANDLERS = new HashMap<>();
相反,它只允许使用未指定的内容:
private static final Map<Class<?>, BiFunction<?, ?, Boolean>> SPECIFIC_HANDLERS = new HashMap<>();
是否有解决方法以便我可以定义BiFunction的两个参数必须是相同的类型,并且Map的键必须是这些参数的类类型?
更新以澄清(因为@Mena的建议不适合我):
我想要一个通用equals helper的数组equals方法的映射。通用助手正式接收两个对象。如果它们是数组,我必须将它传递给一个重载的Arrays.equals()方法。我想找到正确的方法(例1):
private static final Map<Class<?>, BiFunction<?, ?, Boolean>> ARRAY_EQUALS_HANDLER = new HashMap<>();
static
{
ARRAY_EQUALS_HANDLER.put( Object[].class, (l, r) -> Arrays.equals( (Object[]) l, (Object[]) r ));
ARRAY_EQUALS_HANDLER.put( boolean[].class, (l, r) -> Arrays.equals( (boolean[]) l, (boolean[]) r ));
....
}
然后使用它:
boolean equal = ARRAY_EQUALS_HANDLER.get( anObj1.getClass()).apply(anObj1, anObj2);
构造(根据Mena)甚至不编译:
private static <T extends Object> Map<Class<T>, BiFunction<T, T, Boolean>> getSpecificHandlers()
{
Map<Class<T>, BiFunction<T, T, Boolean>> result = new HashMap<>();
result.put( Object[].class, (l, r) -> Arrays.equals( (Object[]) l, (Object[]) r ));
result.put( boolean[].class, (l, r) -> Arrays.equals( (boolean[]) l, (boolean[]) r ));
return result;
}
如果我在方法之外填充生成的地图:
@SuppressWarnings( { "unchecked", "rawtypes" })
private static final Map<Class<?>, BiFunction<?, ?, Boolean>> ARRAY_EQUALS_HANDLER = (Map) getSpecificHandlers();
static
{
ARRAY_EQUALS_HANDLER.put( Object[].class, (l, r) -> Arrays.equals( (Object[]) l, (Object[]) r ));
...
}
然后整个类型的安全性已经消失了,因为在将它分配给最终的静态成员时我必须进行(未经检查的)类型转换。
我上面的示例1有效,但我必须在使用时转换收到的lambda:
private static <T extends Object> boolean equalsArray( T anArray, T anOtherArray) {
Object o = ARRAY_EQUALS_HANDLER.get( anArray.getClass());
@SuppressWarnings( "unchecked")
BiFunction<T, T, Boolean> func = (BiFunction<T, T, Boolean>) o;
Boolean result = func.apply( anArray, anOtherArray);
return result;
}
答案 0 :(得分:3)
您正在使用泛型方法习惯用法(返回类型之前的类型参数声明),用于常量声明。
那个成语不会编译。
如其他地方所述,您不能在静态上下文中使用类泛型类型。
你可以做的解决方法是声明一个静态方法而不是一个常量 - 在以下几行中的内容:
static class WhateverBound{}
static class Foo extends WhateverBound {}
假设:
Map<Class<Foo>, BiFunction<Foo, Foo, Boolean>> map =
getSpecificHandlers(new Foo());
然后您可以调用您的方法:
final
当然请注意,此处的private static final Map<Class<? extends WhateverBound>,
BiFunction<? extends WhateverBound, ? extends WhateverBound, Boolean>>
SPECIFIC_HANDLERS = new HashMap<>();
static {
SPECIFIC_HANDLERS.put(Foo.class, (f, ff) -> true);
}
关键字具有非常不同的含义,可以完全省略。
或... 强>
您可以将其保持为常量,并在所有类型参数化中重复通配符/边界模式。
例如:
var a = moment([2007, 0, 29]);
var b = moment([2007, 0, 28]);
a.diff(b) // 86400000
答案 1 :(得分:0)
我有一个解决方案,它有点难看,但按要求工作。
首先,请注意Map.get()
方法将原始Object
类型作为参数,因此编译器无法根据参数类型猜测此方法的返回类型。而不是返回类型来自字段本身的声明,并且因为它使用固定类型参数声明(在您的情况下它应该是Object
) - 您可以获得.get()
的任何调用的固定返回类型。实际上,同样的事情不仅适用于get()
方法,而且适用于您希望充当类型参数化方法的所有方法。
其次,所需的结构应该对键和值对添加一些限制(您希望值类型依赖于键类型)。这通常通过类型参数完成。它不适用于字段声明,但适用于类型或方法声明。
所以,在这两个前提下,我以解决方案结束:引入扩展Map
类型与其他类型相关的类型以及附加的通用get()
方法(当然,您可以添加更多通用方法)
public static class HandlerMap<T> extends HashMap<Class<? extends T>, BiFunction<T, T, Boolean>> {
@SuppressWarnings("unchecked")
<U extends T> BiFunction<U, U, Boolean> getStrict(Class<? extends U> key) {
return (BiFunction<U, U, Boolean>) get(key);
}
}
private static HandlerMap<Object> ARRAY_EQUALS_HANDLER = new HandlerMap<>();
static {
ARRAY_EQUALS_HANDLER.put(Object[].class, (l, r) -> Arrays.equals((Object[]) l, (Object[]) r));
ARRAY_EQUALS_HANDLER.put(boolean[].class, (l, r) -> Arrays.equals((boolean[]) l, (boolean[]) r));
//WARNING - type safety breaks here
ARRAY_EQUALS_HANDLER.put(int[].class, (l, r) -> Arrays.equals((boolean[]) l, (boolean[]) r));
}
public static void main(String[] args) throws Exception {
BiFunction<int[], int[], Boolean> biFunction = ARRAY_EQUALS_HANDLER.getStrict(int[].class);
}
我直接使用HashMap
作为超类来代替更短的代码。请注意,getStrict()
方法被标记为不安全(显然,它是)。并注意put()
上缺少类型coersion。
我现在想,所有的丑陋都来自于你实际上不想要的Map
,而是一些不同的东西。是的,有一个共同的概念 - 具有键值对的结构,但类型coersion应该以另一种方式工作。 Map
用于将某些类型和值的键限制为另一种特定类型,但您不需要它。你需要的是更强大的东西 - 不仅分别为键和值键入coersion,而且还键入键类型和值类型之间的绑定。
答案 2 :(得分:0)
我不认为你只想使用会员声明就可以了。你可以做的是隐藏地图本身,并为访问地图提供更严格的方法。
例如,像这样:
class FunctionMapper {
private final Map<Class<?>, BiFunction<?, ?, Boolean>> MAP = new HashMap<>();
public <T> void map(Class<T> c, BiFunction<T, T, Boolean> f) {
MAP.put(c, f);
}
public <T> BiFunction<T, T, Boolean> get(Class<T> c) {
return (BiFunction<T, T, Boolean>) MAP.get(c);
}
public <T> Boolean apply(T o1, T o2) {
return get((Class<T>) o1.getClass()).apply(o1, o2);
}
}
在你的平等助手中,你可以像这样使用它:
FunctionMapper mapper = new FunctionMapper(); // make this static final
mapper.map(String.class, (s1, s2) -> s1.compareTo(s2) == 0);
System.out.println(mapper.apply("foo", "bar"));