假设我有这个代码库:
public class DataIn {}
public interface DataOut {}
public class DataSpecial1 implements DataOut {}
public class DataSpecial2 implements DataOut {}
public class TranslatorAndHandler<T extends DataOut>{
public T translate(DataIn dataIn);
public void handle(T t);
}
public class TranslatorImpl1 implements TranslatorAndHandler<DataSpecial1> {
public DataSpecial1 translate(DataIn dataIn){
// Some code
return null;
}
public void handle(DataSpecial1 data){}
}
public class TranslatorImpl2 implements TranslatorAndHandler<DataSpecial2> {
public DataSpecial2 translate(DataIn dataIn){
// Some code
return null;
}
public void handle(DataSpecial2 data){}
}
public class Wrapper {
public static TranslatorAndHandler<? extends DataOut> getCorrectTAH(){
if(someValue) {
return new TranslatorImpl1();
}
return new TranslatorImpl2();
};
private static final TranslatorAndHandler<? extends DataOut> tah = getCorrectTAH();
private DataOut savedData;
public Wrapper(DataIn dataIn) {
savedData = tah.translate(dataIn);
}
public void handleData() {
tah.handle(savedData); // Compiler won't accept this line
}
}
一些上下文:我编写了一个由读者读取的模型格式(DataIn),然后传递给Model(Wrapper)。由于不同的用户将具有不同的OpenGL功能,我将渲染外部化(TranslatorAndHandler)。这将允许我在运行时加载正确的OpenGLHandler(getCorrectTAH()),然后使此Handler将读取的原始数据转换为优化的格式以用于他自己的目的。
任何人都可以告诉代码没有问题,而且它确实是类型安全的。我怎样才能重新设计(重新设计?)我的方法以便这样做?
目前显示此编译器错误:
类型中的方法句柄(捕获#1-of?extends DataOut) TranslatorAndHandler不适用 对于参数(DataOut)
答案 0 :(得分:2)
您将DataOut
类型的实例传递给tah.handle
,但是,它需要它的某个子类型 - 例如,应该清楚以下代码不应该编译:
interface DataOut { }
interface SomeDataOut extends DataOut { }
class TranslatorAndHandler<T extends DataOut> {
public void handle(T t) { }
}
public class Main {
public static void main(String[] args) {
TranslatorAndHandler<SomeDataOut> tah = null;
DataOut t = null;
tah.handle(t);
}
}
确实会导致编译错误:
不兼容的类型:DataOut无法转换为SomeDataOut
可能的解决方法是使Wrapper
类通用,如下所示:
public class Wrapper<T extends DataOut> {
private final TranslatorAndHandler<T> tah = getCorrectTAH();
private T savedData;
public Wrapper(DataIn dataIn) {
savedData = tah.translate(dataIn);
}
public void handleData() {
tah.handle(savedData);
}
}
但是,在这种情况下,tah
字段不能是静态的,因为无法从静态成员引用类型参数。
答案 1 :(得分:1)
泛型是Java中的编译时概念。声明如
public class TranslatorAndHandler<T extends DataOut>{
类型TranslatorAndHandler
的任何变量声明(或表达式)必须(确实应该,不使用原始类型)提供类型参数以绑定到类型参数T
。如果你提供一个,你知道它是什么类型,你可以使用它。如果您提供通配符,则不知道它是什么类型,因此无法使用它。
您的Wrapper
课程必须知道它正在处理的类型。如果在编译时你不知道getCorrectTAH()
返回什么,即。它被宣布为这样
public TranslatorAndHandler<?> getCorrectTAH() {...}
然后你不能期望能够使用TranslatorAndHandler
类型参数,因为它绑定到?
,你在编译时无法知道。
看起来TranslatorAndHandler
首先应该是通用的。对于DataOut
的子类型,它没有什么特别之处。
答案 2 :(得分:0)
通过将包装类更改为以下内容,可以避免编译错误:
public class Wrapper {
// Will be casted
@SuppressWarnings("unchecked")
private static final TranslatorAndHandler<DataOut> tah =
(TranslatorAndHandler<DataOut>) getCorrectTAH();
private DataOut savedData;
public Wrapper(DataIn dataIn) {
savedData = tah.translate(dataIn);
}
public void handleData() {
tah.handle(savedData); // Accepted because we can be sure that this cast will not fail
}
}
如上所述可以肯定这是类型安全的,所以这样就不会出现编译错误。