Java泛型参数类型不匹配,从类通用到方法通用

时间:2019-07-04 06:54:23

标签: java generics casting

我有一个抽象类,它具有类级别的泛型类型<O, P>。在其中,我有一个接受OBiFunction<B, Class<B>, String>的方法。 <B>泛型是在方法上定义的。

public interface ParamDescription<O, P> {
    //...
    <B> String getParamString(O obj, BiFunction<B, Class<B>, String> objectToString);
}

然后,我有三个类以不同的方式实现该接口,但是它们都具有Class<O>Class<P>变量。两个也有一些额外的类级别的泛型类型,Class<>变量也与它们一起使用。在所有这些方法中,我试图使用一个类级别的泛型类型及其关联的类变量的对象来调用objectToString方法。

这是最简单的一个:

Function<? super O, P> getter;
Class<P> paramClass;
public <B> String getParamString(O obj, BiFunction<B, Class<B>, String> objectToString) {
    return objectToString.apply(getter.apply(obj), paramClass);
}

但是,当我尝试编译它时,我得到了:

error: method apply in interface BiFunction<T,U,R> cannot be applied to given types;
        return objectToString.apply(getter.apply(obj), paramClass);
                             ^
  required: B,Class<B>
  found: P,Class<P>
  reason: argument mismatch; P cannot be converted to B
  where B,O,P,T,U,R are type-variables:
    B extends Object declared in method <B>getParamString(O,BiFunction<B,Class<B>,String>)
    O extends Object declared in class ParamDescriptionSingle
    P extends Object declared in class ParamDescriptionSingle
    T extends Object declared in interface BiFunction
    U extends Object declared in interface BiFunction
    R extends Object declared in interface BiFunction

在此示例中,我可以通过将B更改为P来解决此问题。但是,这不适用于其他两个实现。在其他两种实现中,我需要使用一些接口不知道的通用类型。

我唯一真正的限制是,提供给BiFunction的Class<B>参数应该与也赋予它的B参数相对应。基本上,“这是一个对象,这是它的类型。”

我尝试创建这样的辅助函数

public <B> String getParamString(O obj, BiFunction<B, Class<B>, String> objectToString) {
    return paramToString(obj, objectToString);
}
public String paramToString(O obj, BiFunction<P, Class<P>, String> objectToString) {
    return objectToString.apply(getter.apply(obj), paramClass);
}

但是它不喜欢BiFunction上的强制转换。

我也尝试过这样的实际转换:

public <B> String getParamString(O obj, BiFunction<B, Class<B>, String> objectToString) {
    return objectToString.apply((B)getter.apply(obj), (Class<B>)paramClass);
}

但是这会给两个强制转换都提供未经检查的强制转换警告,而且我还不知道它是否真正安全。我的理解是,它基本上只会将第一件事投给Object,然后将第二件事投给Class

是强制转换和禁止显示警告的方法还是更好的方法?

作为参考,这是我的其他两个实现:

//Class level: <O, E, P extends Collection<? extends E>>
Class<O> objClass;
Class<P> paramClass;
Class<E> entryClass;
Function<? super O, P> getter;
public <B> String getParamString(O obj, BiFunction<B, Class<B>, String> objectToString) {
    P collection = getter.apply(obj);
    if (collection == null) {
        return objectToString.apply(collection, paramClass);
    }
    return collection.stream()
                     .map(e -> objectToString.apply(e, entryClass))
                     .collect(Collectors.toList())
                     .toString();
}

//Class level: <O, K, V, P extends Map<? extends K, ? extends V>>
Class<O> objClass;
Class<P> paramClass;
Class<K> keyClass;
Class<V> valueClass;
Function<? super O, P> getter;
public <B> String getParamString(O obj, BiFunction<B, Class<B>, String> objectToString) {
    P map = getter.apply(obj);
    if (map == null) {
        return objectToString.apply(map, paramClass);
    }
    return map.entrySet()
              .stream()
              .collect(Collectors.toMap(e -> objectToString.apply(e.getKey(), keyClass),
                                        e -> objectToString.apply(e.getValue(), valueClass)))
              .toString();
}

用我尝试过的更多尝试更新了1,但没有成功:

我尝试设置目标:

public <B> String getParamString(O obj, BiFunction<B, Class<B>, String> objectToString) {
    return objectToString.<P>apply(getter.apply(obj), paramClass);
}

但这仍然可以

required: B,Class<B>
found: P,Class<P>
reason: argument mismatch; P cannot be converted to B

我尝试使用通配符代替B

public String getParamString(O obj, BiFunction<?, Class<?>, String> objectToString) {
    return objectToString.apply(getter.apply(obj), paramClass);
}

具有相同的结果:

required: CAP#1,Class<?>
found: P,Class<P>
reason: argument mismatch; P cannot be converted to CAP#1

看起来明确地强制转换和禁止显示警告可能是唯一的方法。

@SuppressWarnings("unchecked")
public <B> String getParamString(O obj, BiFunction<B, Class<B>, String> objectToString) {
    return objectToString.apply((B)getter.apply(obj), (Class<B>)paramClass);
}

但是,这令人沮丧,因为然后我失去了编译时检查,即所提供的类(第二个参数)代表第一个参数的类。但是它确实可以正常工作,否则可以正常工作。

用我的经历更新2 由于强制转换是我可以实际使用的唯一方法,并且消除了上面提到的检查,因此我决定完全摆脱通用类型B

我的界面有这个:

String getParamString(O obj, BiFunction<Object, Class, String> objectToString);

我的实现如下:

public String getParamString(O obj, BiFunction<Object, Class, String> objectToString) {
    return objectToString.apply(getter.apply(obj), paramClass);
}

public String getParamString(O obj, BiFunction<Object, Class, String> objectToString) {
    P collection = getter.apply(obj);
    if (collection == null) {
        return objectToString.apply(collection, paramClass);
    }
    return collection.stream()
                     .map(e -> objectToString.apply(e, entryClass))
                     .collect(Collectors.toList())
                     .toString();
}

public String getParamString(O obj, BiFunction<Object, Class, String> objectToString) {
    P map = getter.apply(obj);
    if (map == null) {
        return objectToString.apply(map, paramClass);
    }
    return map.entrySet()
              .stream()
              .collect(Collectors.toMap(e -> objectToString.apply(e.getKey(), keyClass),
                                        e -> objectToString.apply(e.getValue(), valueClass)))
              .toString();
}

然后,在调用这些函数时,我必须禁止显示警告,但这是由于我如何创建BiFunction。我正在使用另一种方法来创建它,该方法采用通用方法PClass<P>,对于BiFunction,这些方法将被强制转换为Object和原始Class。不过,我认为,这比每次使用BiFunction都要强制转换和禁止显示警告要好。

总而言之:我做到了,但我无法以提供所需保护的方式来实现它。

1 个答案:

答案 0 :(得分:0)

简短的回答:请勿在该方法上使用通用类型B

详细信息: 除了强制转换之外,没有办法告诉编译器对于给定的调用,应该将P(或其他某种通用类型)视为B

return objectToString.apply((B)getter.apply(obj), (Class<B>)paramClass);

但是,由于要显式进行强制转换,因此无法保证所提供的类(第二个参数)代表第一个参数的类。定义B泛型类型的唯一目的是获得这种保护。因此,最好不要在此处使用泛型,而应将参数设为BiFunction<Object, Class, String>

String getParamString(O obj, BiFunction<Object, Class, String> objectToString);