問題描述
匿名類作為泛型參數 (anonymous class as generic parameter)
我想創建一個從匿名類定義中獲取對象的類來存儲。我使用了一個泛型類型的類來實現這一點。然後我想使用功能接口定義一些操作,這些接口將這個對像作為參數來使用。
代碼比文字更重要。所以看看這個:
public class Test<T> {
@FunctionalInterface
public interface operation<T> {
void execute(T object);
}
private T obj;
public Test(T _obj){
obj = _obj;
}
public void runOperation(operation<T> op){
op.execute(obj);
}
public static void main(String[] args){
Test<?> t = new Test<>(new Object(){
public String text = "Something";
});
t.runOperation((o) ‑> {
System.out.println(o.text); // text cannot be resolved
});
}
}
我的問題是在功能接口的實現中o.text無法解決。這是某種類型擦除的後果嗎?
有趣的是,當我在構造函數中實現功能接口時,我可以讓這段代碼工作。
看看這段代碼:
public class Test<T> {
@FunctionalInterface
public interface operation<T> {
void execute(T object);
}
private T obj;
private operation<T> op;
public Test(T _obj, operation<T> _op){
obj = _obj;
op = _op;
}
public void runOperation(){
op.execute(obj);
}
public static void main(String[] args){
Test<?> t = new Test<>(new Object(){
public String text = "Something";
}, (o) ‑> {
System.out.println(o.text);
});
t.runOperation();
}
}
這很完美,可以打印出“某事”。但是我的第一種方法有什麼問題?我真的不明白這裡的問題。
參考解法
方法 1:
The problem is that your anonymous class still has to conform to (extend or implement) some type, and the type you've chosen is Object
which doesn't have your text
property. In order to refer to properties of some kind, you'll need an actual class or interface to work with, so the compiler can make guarantees about what properties and methods are available on the object.
This works.
public class Test<T> {
public static class Data {
public String text;
}
@FunctionalInterface
public interface Operation<K> {
void execute(K object);
}
private T obj;
private Operation<T> op;
public Test(T obj) {
this.obj = obj;
}
public void runOperation(Operation<T> op) {
op.execute(obj);
}
public static void main(String[] args) {
Test<Data> t = new Test<>(new Data() {{
this.text = "Something";
}});
t.runOperation((o) ‑> {
System.out.println(o.text);
});
}
}
方法 2:
Test<?> t = new Test<>(new Object(){
public String text = "Something";
}, (o) ‑> {
System.out.println(o.text);
});
The compiler here is replacing T
in Test
with your anonymous class and since that class contains a variable text
that's why the 2nd case works.
方法 3:
In the second piece of code,
new Test<>(new Object(){
public String text = "Something";
}, (o) ‑> {
System.out.println(o.text);
});
compiles because the type argument of Test
for the constructor call is inferred (since the diamond operator is used), and it is inferred to the anonymous type that the first argument evaluates to (the anonymous class type), and thus the second argument's type is operation<that anonymous class type>
, which works.
In the first piece of code, the expression
t.runOperation((o) ‑> {
System.out.println(o.text); // text cannot be resolved
})
does not compile. Here, the type of the lambda is inferred based on the type of the variable t
, which is Test<?>
. Thus, the argument of runOperation
must be operation<some unknown type>
. The only argument to runOperation
that will work here is null
.
(by ArcticLord、Ian McLaird、Ramanlfc、newacct)