我有一个看起来像这样的课......
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很陌生。)
答案 0 :(得分:0)
正如帖子标题所示,我被告知我“无法实例化T型”。从其他Stack Exchange帖子中,我发现由于泛型如何工作,这是不可能的。
这是因为Java中的泛型纯粹是编译时功能;编译器抛弃了泛型(这称为“type erasure”),因此在运行时,不存在类型变量T
,因此您无法执行new T(...)
。
您可以通过将Class<T>
对象传递给需要创建T
实例的方法,然后通过reflection创建实例来在Java中执行此操作。
答案 1 :(得分:0)
在第二个代码示例中,编译器无法知道T
是否具有接受recordDelimiterBytes
作为参数的构造函数。这是因为每个类都是一个单独的编译单元,因此在编译LegionInputFormat
时,编译器只知道T
是RecordReader<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>
。