[英]Generic FunctionalInterface and Method Reference messed up by Type Erasure
[英]Generic FunctionalInterface and type erasure
以下編譯錯誤是什么意思?
FooSetter類型的方法apply(Sample.Foo,capture#5-of?)不適用於參數(Sample.Foo,capture#6-of?)
它發生在下面的代碼中(在我用注釋指示的底部),該代碼嘗試將一個bean的setter映射到另一個bean的getter而不進行轉換(是的,我知道有實用程序可以執行此操作,但這是一個示例)。 我使用以下兩個功能接口
@FunctionalInterface
public interface FooSetter<V> {
void apply(Foo foo, V value);
}
@FunctionalInterface
public interface BarGetter<T> {
T apply(Bar from);
}
public class Sample {
public static class Bar{
public String description;
public Integer num;
public String getDecription(){
return description;
}
public void setDescription(String desc){
this.description = desc;
}
public Integer getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
public static class Foo{
private String name;
private Integer age;
public Foo setName(String name){
this.name = name;
return this;
}
public Foo setAge(int age){
this.age = age;
return this;
}
@Override
public String toString(){
return "Name" + name + " Age " + age;
}
}
public static void main(String[] args) throws Exception {
HashMap<String, BarGetter<?>> barGetters= Maps.newHashMap();
barGetters.put("description", (BarGetter<String>)(Bar b) -> b.getDecription());
barGetters.put("num", (BarGetter<Integer>)(Bar b) -> b.getNum());
HashMap<String, FooSetter<?>> fooSetters= Maps.newHashMap();
fooSetters.put("name", (Foo f, String name) -> f.setName(name));
fooSetters.put("age", (Foo f, Integer age) -> f.setAge(age));
HashMap<String,String> fooFromBar = Maps.newHashMap();
// FOO BAR
fooFromBar.put("name", "description");
fooFromBar.put("age", "num");
Bar bar = new Bar();
bar.setDescription("bar desc");
bar.setNum(5);
Foo newFoo = new Foo();
for (String key : fooFromBar.keySet()) {
//COMPILATION ERROR
fooSetters.get(key).apply(newFoo, barGetters.get( fooFromBar.get(key) ).apply(bar) );
}
}
}
關於類型擦除,我在這里沒有看到什么嗎?
這個
fooSetters.get(key)
返回FooSetter<?>
類型的值,即。 一個不知道其設置類型的FooSetter
。 因此,其apply
方法具有以下(推斷的)定義
void apply(Foo foo, ? value);
您不能將此方法與任何方法(用於其第二個參數)一起使用,但不能將其與null
(可以用於任何引用類型)一起使用,因為您不知道它是什么類型。
這個
barGetters.get( fooFromBar.get(key) )
返回BarGetter<?>
類型的值,即不知道所得到的BarGetter
。 因此,其apply
方法顯示為
? apply(Bar from);
這個?
可能與fooSetter.apply
期望的完全不同。
因此,類型系統不允許這樣做,並給您帶來編譯錯誤。
如果您具有不同類型的通用元素的集合,則需要一個幫助程序class
, class
包裝實際元素並使用運行時檢查來保護其訪問,該運行時檢查替換了在這種情況下不起作用的編譯時檢查。
例如
final class FooSetterHolder<T> {
final Class<T> guard;
final FooSetter<T> setter;
FooSetterHolder(Class<T> guard, FooSetter<T> setter) {
this.guard = guard;
this.setter = setter;
}
<U> FooSetterHolder<U> cast(Class<U> expectedType) {
if(guard != expectedType) throw new ClassCastException(
"cannot cast FooSetterHolder<"+guard.getName()
+"> to FooSetterHolder<"+expectedType.getName()+'>');
// now that we have checked the type
@SuppressWarnings("unchecked") FooSetterHolder<U> h=(FooSetterHolder)this;
return h;
}
}
您可以通過以下方式使用此幫助程序類:
HashMap<String, FooSetterHolder<?>> fooSetters= new HashMap<>();
fooSetters.put("name", new FooSetterHolder<>(String.class, Foo::setName));
fooSetters.put("age", new FooSetterHolder<>(Integer.class, Foo::setAge));
FooSetter<String> nameSetter=fooSetters.get("name").cast(String.class).setter;
FooSetter<Integer> ageSetter=fooSetters.get("age").cast(Integer.class).setter;
該cast
方法提供了內部使用不可避免未經檢查的操作,但使得使用它的代碼是類型安全(假設沒有其它未選中的操作)執行期望的和實際類型的運行時的比較的操作。
因此,以下代碼不會被編譯器拒絕
FooSetter<Integer> wrong=fooSetters.get("name").cast(Integer.class).setter;
但是不會造成堆污染 ,而是引發ClassCastException
,消息為“ cannot cast FooSetterHolder<java.lang.String> to FooSetterHolder<java.lang.Integer>
”,這與普通的非泛型類型轉換非常相似。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.