无法实例化类型T

时间:2016-12-13 12:57:02

标签: java hadoop generics

我有一个看起来像这样的课......

public class LegionInputFormat
        extends FileInputFormat<NullWritable, LegionRecord> {

    @Override
    public RecordReader<NullWritable, LegionRecord>
            createRecordReader(InputSplit split, TaskAttemptContext context) {

        /* Skipped code for getting recordDelimiterBytes */

        return new LegionRecordReader(recordDelimiterBytes);
    }
}

我想使用泛型类型,因此它可以返回用户指定的任何类型的RecordReader,如下所示:

public class LegionInputFormat<T extends RecordReader<NullWritable, LegionRecord>>
        extends FileInputFormat<NullWritable, LegionRecord> {

    @Override
    public RecordReader<NullWritable, LegionRecord>
            createRecordReader(InputSplit split, TaskAttemptContext context) {

        /* Skipped code for getting recordDelimiterBytes */

        return new T(recordDelimiterBytes);
    }
}

正如帖子标题所示,我被告知我“无法实例化T型”。从其他Stack Exchange帖子中,我发现由于泛型如何工作,这是不可能的。我无法收集的是一个直观的解释,为什么会这样。我通过理解学得最好,所以如果有人可以提供,那将会非常有用。

我也对完成我在这里要做的事情的最佳实践感兴趣。 LegionInputFormat的构造函数是否应接受RecordReader类,存储该类,然后稍后引用它以创建新实例?或者有更好的解决方案吗?

(其他背景 - 上下文是Hadoop,但我怀疑它是否重要。我是一位相当成熟的数据科学家,但我对Java很陌生。)

2 个答案:

答案 0 :(得分:0)

  

正如帖子标题所示,我被告知我“无法实例化T型”。从其他Stack Exchange帖子中,我发现由于泛型如何工作,这是不可能的。

这是因为Java中的泛型纯粹是编译时功能;编译器抛弃了泛型(这称为“type erasure”),因此在运行时,不存在类型变量T,因此您无法执行new T(...)

您可以通过将Class<T>对象传递给需要创建T实例的方法,然后通过reflection创建实例来在Java中执行此操作。

答案 1 :(得分:0)

在第二个代码示例中,编译器无法知道T是否具有接受recordDelimiterBytes作为参数的构造函数。这是因为每个类都是一个单独的编译单元,因此在编译LegionInputFormat时,编译器只知道TRecordReader<NullWritable, LegionRecord>。它不知道T使用了哪些具体类型,并且必须假设某个人以后可以在任何扩展RecordReader<NullWritable, LegionRecord>的类中。我们可以使用T告诉编译器关于extends的一些内容,但是在Java中我们无法指定T具有构造函数T(byte[])(或者任何类型的recordDelimiterBytes是。)

我已经使用了以下几个解决方案,即使它需要创建子类,我也很满意。这项工作仍然属于通用类。它现在被宣布为抽象:

public abstract class InputFormat<T extends RecordReader<NullWritable, LegionRecord>>
        extends FileInputFormat<NullWritable, LegionRecord> {

    private byte[] recordDelimiterBytes;

    @Override
    public RecordReader<NullWritable, LegionRecord> createRecordReader(InputSplit split, TaskAttemptContext context) {

        /* Skipped code for getting recordDelimiterBytes */

        return constructRecordReader(recordDelimiterBytes);
    }

    // factory method for T objects
    protected abstract RecordReader<NullWritable, LegionRecord> constructRecordReader(byte[] recordDelimiterBytes);
}

对于实例化,它要求你只用以下几行编写一个具体的子类:

public class LegionInputFormat extends InputFormat<LegionRecordReader> {

    @Override
    protected RecordReader<NullWritable, LegionRecord> constructRecordReader(byte[] recordDelimiterBytes) {
        return new LegionRecordReader(recordDelimiterBytes);
    }

}

在子类中,我们知道T的具体类,因此知道类的​​构造函数/ s,因此我们可以实例化它。虽然不像你希望的那么简单,但我认为解决方案很好而且干净。

在我自己的代码中,我借此机会将工厂方法声明为返回类型T

protected abstract T constructRecordReader(byte[] recordDelimiterBytes);

然后你只需要跟进实施:

protected LegionRecordReader constructRecordReader(byte[] recordDelimiterBytes) {

在这种情况下,它甚至可以缩短几个字符。另一方面,在您的情况下,您似乎不需要它,因此您可能更愿意使用较弱的返回类型RecordReader<NullWritable, LegionRecord>