静态成员中的通用参数声明

为什么不允许定义这样一个静态成员:

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的两个参数必须是相同的类型,并且映射的键必须是这些参数的类类型?

更新澄清(因为@Mena的建议不适合我):

我想要一个泛型等号助手的数组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;
}

您正在使用泛型方法惯用法(返回类型之前的类型参数声明),用于常量声明。

这个习语不会编译。

正如其他地方所提到的,您不能在静态上下文中使用类泛型类型。

可以做一个解决方法是声明一个静态方法而不是一个常量 - 在下面的行中:

private static final <T extends WhateverBound> Map<Class<T>, 
    BiFunction<T, T, Boolean>> 
    getSpecificHandlers(T t) {

    // return new HashMap<Class<T>, BiFunction<T, T, Boolean>>();
    // much easier on the eye - thanks Andy Turner
    return new HashMap<>();
}

假设:

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

我有一个解决方案,它有点难看,但按要求工作。

首先,请注意, Map.get()方法将原始Object类型作为参数,因此编译器不可能根据参数类型来猜测此方法的返回类型。 而不是返回类型来自声明字段本身,并且由于它使用固定类型参数(在您的情况下它应该是Object )声明 - 您可以获得任何调用.get()固定返回类型。 实际上,同样的事情不仅适用于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()上缺少type coersion。

我想现在,所有这些丑陋都来自你实际上不想要的Map ,但有些不同。 是的,有一个共同的概念 - 有键值对的结构,但类型转换应该以另一种方式工作。 用于将键限制为某种类型和值的Map为另一种类型的Map ,但您不需要它。 你需要的是更强大的东西 - 不仅为键和值分别键入coersion,而且还在键类型和值类型之间进行绑定。


我不认为你只需要使用成员声明即可。 你可以做的是隐藏地图本身,并提供更严格的方法来访问地图。

例如,像这样:

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"));
链接地址: http://www.djcxy.com/p/32501.html

上一篇: Generic parameters declaration in static member

下一篇: Running Selenium headless with multiple spiders