本文介绍了静态成员中的通用参数声明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Why it is not allowed to define such a static member:

private static final <T extends Object> Map<Class<T>, BiFunction<T, T, Boolean>> SPECIFIC_HANDLERS = new HashMap<>();

相反,只允许未指定使用它:

Instead it's only allowed to use it unspecified:

private static final Map<Class<?>, BiFunction<?, ?, Boolean>> SPECIFIC_HANDLERS = new HashMap<>();

是否有解决方法,以便我可以定义BiFunction的两个参数必须为同一类型,并且Map的键必须为这些参数的类类型?

Is there a workaround so that I can define that both parameters to the BiFunction MUST be of the same type and that the key of the Map MUST be the class type of these parameters?

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

我想要通用equals助手的array equals方法的映射.通用帮助器正式接收两个对象.如果它们是数组,则必须将其传递给重载的Arrays.equals()方法之一.我想要查找正确的方法(示例1):

I want a map for the array equals method for a generic equals helper. The generic helper receives formally two Objects. If they are arrays I have to pass it to one of the overloaded Arrays.equals() method. I wandted to have a lookup for the correct method (Example 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)甚至无法编译:

The construct (according to Mena) does not even compile:

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

如果我在方法之外填充生成的地图:

And if I populate the generated map outside the method:

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

然后整个类型的安全性消失了,因为在将其分配给最终的静态成员时,我必须进行(未经检查的)类型转换.

then the whole type safety has gone because I have to do a (unchecked) typecast when assigning it to the final static member.

上面的示例1可以正常工作,但是我必须在使用它时强制转换接收到的lambda:

My Example 1 above works, but I have to cast the received lambda when using it :

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

推荐答案

我有一个解决方案,虽然有点丑陋,但可以按要求工作.

I have a solution, it's a bit ugly, but works as requested.

首先,请注意,Map.get()方法采用原始的Object类型作为参数,因此编译器无法根据参数类型猜测此方法的返回类型.而不是该返回类型来自字段本身的声明,并且它是使用固定类型参数声明的(在您的情况下,它应为Object)-您可以为.get()的任何调用获得固定的返回类型.实际上,同样的事情不仅适用于get()方法,而且适用于希望用作类型参数化方法的所有对象.

First thing, note that Map.get() method takes raw Object type as argument, thus it is not possible for compiler to guess return type of this method based on the argument type. Instead of that return type comes from declaration of the field itself, and as it's declared using fixed type parameter (in your case it should be Object) - you get fixed return type for any invocation of .get(). Actually, same thing applies not only for get() method but for all you expect to act as type-parameterized methods.

第二,所需的结构应在键和值对上增加一些限制(您希望值类型取决于键类型).这通常是通过类型参数完成的.它不适用于字段声明,但适用于类型或方法声明.

Secondly, desired structure should add some restrictions on key and value pairs (you want value type to depend on the key type). This is usually done via type parameters. It doesn't work for field declaration but does work for type or method declaration.

因此,在这两个前提下,我以解决方案结尾:介绍扩展了Map的类型,该类型扩展了具有附加的类型相关性和附加的通用get()方法(当然,您可以添加更多的通用方法).

So, with those two premnises, I end with the solution: introduce type that extends Map with additional type correlation and with additional generic get() method (you may add more generic methods, of course).

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()上也缺少类型强制.

I used HashMap as superclass directly for shorter code. Note that getStrict() method is marked as unsafe (obviously, it is). And note also missing type coersion on put().

我现在认为,所有的丑陋源于您实际上不想要Map而是某种不同的东西.是的,有一个通用概念-具有键值对的结构,但是类型强制应该以另一种方式起作用. Map用于将键限制为某种类型,将值限制为另一种特定类型,但是您不需要它.您需要的是更强大的功能-不仅分别对键和值进行类型强制转换,而且还对键类型和值类型进行绑定.

I think now, that all that ugliness comes from that you actually want not Map, but something different. Yes, there is a common concept - structure with key-value pairs, but type coersion should work another way. Map used to restrict keys to certain type and values to another certain type, but you don't need it. What you need is something stronger - not only type coersion for keys and values separately, but also binding between key type and value type.

这篇关于静态成员中的通用参数声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-27 21:31
查看更多