通用功能接口和类型擦除
Generic FunctionalInterface and type erasure
下面的编译错误是什么意思?
FooSetter 类型中的方法 apply(Sample.Foo, capture#5-of ?) 不适用于参数 (Sample.Foo,
捕获#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)
returns 类型 FooSetter<?>
的值,即。不知道它设置的类型的 FooSetter
。因此,其 apply
方法具有以下(推断的)定义
void apply(Foo foo, ? value);
除了 null
(可用于任何引用类型)之外,您不能将此方法用于任何东西(对于它的第二个参数),因为您不知道它是什么类型。
这个
barGetters.get( fooFromBar.get(key) )
returns 类型 BarGetter<?>
的值,即 BarGetter
不知道它得到了什么。它的 apply
方法因此显示为
? apply(Bar from);
这 ?
可能与 fooSetter.apply
的预期完全不同。
类型系统因此不允许它并给你一个编译错误。
如果您有一组不同类型的通用元素,则需要一个帮助程序 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;
}
}
您可以通过以下方式使用此助手class:
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>
”,这与普通的非通用类型非常相似演员表。
下面的编译错误是什么意思?
FooSetter 类型中的方法 apply(Sample.Foo, capture#5-of ?) 不适用于参数 (Sample.Foo, 捕获#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)
returns 类型 FooSetter<?>
的值,即。不知道它设置的类型的 FooSetter
。因此,其 apply
方法具有以下(推断的)定义
void apply(Foo foo, ? value);
除了 null
(可用于任何引用类型)之外,您不能将此方法用于任何东西(对于它的第二个参数),因为您不知道它是什么类型。
这个
barGetters.get( fooFromBar.get(key) )
returns 类型 BarGetter<?>
的值,即 BarGetter
不知道它得到了什么。它的 apply
方法因此显示为
? apply(Bar from);
这 ?
可能与 fooSetter.apply
的预期完全不同。
类型系统因此不允许它并给你一个编译错误。
如果您有一组不同类型的通用元素,则需要一个帮助程序 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;
}
}
您可以通过以下方式使用此助手class:
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>
”,这与普通的非通用类型非常相似演员表。