我有一个抽象类,它具有类级别的泛型类型<O, P>
。在其中,我有一个接受O
和BiFunction<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。我正在使用另一种方法来创建它,该方法采用通用方法P
和Class<P>
,对于BiFunction,这些方法将被强制转换为Object
和原始Class
。不过,我认为,这比每次使用BiFunction都要强制转换和禁止显示警告要好。
总而言之:我做到了,但我无法以提供所需保护的方式来实现它。
答案 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);