通用功能接口和方法参考被类型擦除弄乱了
Generic FunctionalInterface and Method Reference messed up by Type Erasure
我有以下通用功能接口:
@FunctionalInterface
public interface FooInterface<T> {
void bar(T arg);
}
而这个 ArrayList 后代:
public class FooList<T> extends ArrayList<FooInterface<T>> {
public void doFoo(T arg) {
for(Iterator<FooInterface<T>> i = iterator(); i.hasNext(); ) {
i.next().bar(arg);
}
}
}
现在,我使用方法引用和类型擦除编写这段代码:
protected void doFoo(Object arg) { }
private void doStuff() {
FooInterface f = this::doFoo;
List<FooInterface> list = new ArrayList<>();
list.add(f2);
list.add(this::doFoo);
FooList list2 = new FooList();
list2.add(f2);
list2.add(this::doFoo); // <-- Compiler chokes here, complaining that this is not a FunctionalInterface
}
这让我很困惑。为什么编译器会同意我将 this::doFoo 分配给 FooInterface 变量,并在代码的第一部分调用 List.add(),只是拒绝从 [= 调用相同的 add() 方法25=] 是 ArrayList 的后代?
我的后代 class 中的类型擦除似乎发生了一些奇怪的事情,但是什么?这是一个错误吗?我做了一些不受支持的事情吗?
FooList
(没有类型参数)被称为原始类型。 4.8. Raw Types 是这样说的:
The superclasses (respectively, superinterfaces) of a raw type are the erasures of the superclasses (superinterfaces) of any of the parameterizations of the generic type.
这意味着原始 FooList
只是原始 ArrayList
并且方法 add
接受 Object
.
因为 Object
不是函数式接口,所以它不能成为 lambda 的目标。这也行不通:
Object f = this::doFoo;
完整的编译器错误或多或少证实了这一切:
error: no suitable method found for add(this::doFoo)
list2.add(this::doFoo); // <-- Compiler chokes here, complaining that this is not a FunctionalInterface
^
method <strong>Collection.add(Object)</strong> is not applicable
(argument mismatch; <strong>Object is not a functional interface</strong>)
实现 "fix" 的一种方法是执行如下操作:
public class FooList<T> extends ArrayList<FooInterface<T>> {
@Override
public boolean add(FooInterface<T> e) {
return super.add(e);
}
...
}
这里的真正解决方案是 not use raw types,但既然你提到了 'erasure',那么你似乎在某种程度上意识到了这一点。没有理由使用原始类型。
您需要参数化 FooList。如果你改变
FooList list2 = new FooList();
至
FooList<FooInterface> list2 = new FooList();
它将消除编译器错误。
我有以下通用功能接口:
@FunctionalInterface
public interface FooInterface<T> {
void bar(T arg);
}
而这个 ArrayList 后代:
public class FooList<T> extends ArrayList<FooInterface<T>> {
public void doFoo(T arg) {
for(Iterator<FooInterface<T>> i = iterator(); i.hasNext(); ) {
i.next().bar(arg);
}
}
}
现在,我使用方法引用和类型擦除编写这段代码:
protected void doFoo(Object arg) { }
private void doStuff() {
FooInterface f = this::doFoo;
List<FooInterface> list = new ArrayList<>();
list.add(f2);
list.add(this::doFoo);
FooList list2 = new FooList();
list2.add(f2);
list2.add(this::doFoo); // <-- Compiler chokes here, complaining that this is not a FunctionalInterface
}
这让我很困惑。为什么编译器会同意我将 this::doFoo 分配给 FooInterface 变量,并在代码的第一部分调用 List.add(),只是拒绝从 [= 调用相同的 add() 方法25=] 是 ArrayList 的后代?
我的后代 class 中的类型擦除似乎发生了一些奇怪的事情,但是什么?这是一个错误吗?我做了一些不受支持的事情吗?
FooList
(没有类型参数)被称为原始类型。 4.8. Raw Types 是这样说的:
The superclasses (respectively, superinterfaces) of a raw type are the erasures of the superclasses (superinterfaces) of any of the parameterizations of the generic type.
这意味着原始 FooList
只是原始 ArrayList
并且方法 add
接受 Object
.
因为 Object
不是函数式接口,所以它不能成为 lambda 的目标。这也行不通:
Object f = this::doFoo;
完整的编译器错误或多或少证实了这一切:
error: no suitable method found for add(this::doFoo)
list2.add(this::doFoo); // <-- Compiler chokes here, complaining that this is not a FunctionalInterface
^
method <strong>Collection.add(Object)</strong> is not applicable
(argument mismatch; <strong>Object is not a functional interface</strong>)
实现 "fix" 的一种方法是执行如下操作:
public class FooList<T> extends ArrayList<FooInterface<T>> {
@Override
public boolean add(FooInterface<T> e) {
return super.add(e);
}
...
}
这里的真正解决方案是 not use raw types,但既然你提到了 'erasure',那么你似乎在某种程度上意识到了这一点。没有理由使用原始类型。
您需要参数化 FooList。如果你改变
FooList list2 = new FooList();
至
FooList<FooInterface> list2 = new FooList();
它将消除编译器错误。