通用类型:捕获属性类型

时间:2014-07-20 16:13:54

标签: java generics compiler-errors

假设我有这个代码库:

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)

3 个答案:

答案 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
    }
}

如上所述可以肯定这是类型安全的,所以这样就不会出现编译错误。