无法示例化类型t

r8uurelv  于 2021-05-29  发布在  Hadoop
关注(0)|答案(2)|浏览(422)

我有一节课看起来像这样。。。

  1. public class LegionInputFormat
  2. extends FileInputFormat<NullWritable, LegionRecord> {
  3. @Override
  4. public RecordReader<NullWritable, LegionRecord>
  5. createRecordReader(InputSplit split, TaskAttemptContext context) {
  6. /* Skipped code for getting recordDelimiterBytes */
  7. return new LegionRecordReader(recordDelimiterBytes);
  8. }
  9. }

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

  1. public class LegionInputFormat<T extends RecordReader<NullWritable, LegionRecord>>
  2. extends FileInputFormat<NullWritable, LegionRecord> {
  3. @Override
  4. public RecordReader<NullWritable, LegionRecord>
  5. createRecordReader(InputSplit split, TaskAttemptContext context) {
  6. /* Skipped code for getting recordDelimiterBytes */
  7. return new T(recordDelimiterBytes);
  8. }
  9. }

正如文章标题所示,我被告知“无法示例化t类型”。从其他堆栈交换文章中,我发现这是不可能的,因为泛型是如何工作的。我没能收集到的是为什么会这样的直观解释。我通过理解才能学得最好,所以如果有人能提供帮助,那将是非常有帮助的。
我还对实现我在这里要做的事情的最佳实践感兴趣。构造器应该 LegionInputFormat 接受 RecordReader 类,存储它,然后稍后引用它以创建新示例?还是有更好的解决办法?
(其他背景-这里的上下文是hadoop,但我怀疑它是否重要。我是一个相当有成就的数据科学家,但我对java还很陌生。)

slwdgvem

slwdgvem1#

在第二个代码示例中,编译器无法知道 T 有一个接受 recordDelimiterBytes 作为论据。这是因为每个类都是一个独立的编译单元 LegionInputFormat 是编译的,编译器只知道 T 是一个 RecordReader<NullWritable, LegionRecord> . 它不知道哪种混凝土是用来 T ,它必须假设以后有人可以加入任何扩展的类 RecordReader<NullWritable, LegionRecord> . 我们可以告诉编译器 T 使用 extends ,但在java中,我们无法指定 T 有一个构造函数 T(byte[]) (或任何类型的 recordDelimiterBytes 是)。
我已经多次使用下面的解决方案,尽管它需要创建子类,但我对它非常满意。这项工作仍在泛型类中。现宣布为摘要:

  1. public abstract class InputFormat<T extends RecordReader<NullWritable, LegionRecord>>
  2. extends FileInputFormat<NullWritable, LegionRecord> {
  3. private byte[] recordDelimiterBytes;
  4. @Override
  5. public RecordReader<NullWritable, LegionRecord> createRecordReader(InputSplit split, TaskAttemptContext context) {
  6. /* Skipped code for getting recordDelimiterBytes */
  7. return constructRecordReader(recordDelimiterBytes);
  8. }
  9. // factory method for T objects
  10. protected abstract RecordReader<NullWritable, LegionRecord> constructRecordReader(byte[] recordDelimiterBytes);
  11. }

对于示例化,它需要您编写一个具体的子类,其中只有以下几行:

  1. public class LegionInputFormat extends InputFormat<LegionRecordReader> {
  2. @Override
  3. protected RecordReader<NullWritable, LegionRecord> constructRecordReader(byte[] recordDelimiterBytes) {
  4. return new LegionRecordReader(recordDelimiterBytes);
  5. }
  6. }

在子类中,我们知道 T 因此类的构造函数,所以我们可以示例化它。虽然没有你所希望的那么简单,但我认为这个解决方案很好,很干净。
在我自己的代码中,我利用这个机会将工厂方法声明为返回类型 T :

  1. protected abstract T constructRecordReader(byte[] recordDelimiterBytes);

然后您只需在实施过程中跟进:

  1. protected LegionRecordReader constructRecordReader(byte[] recordDelimiterBytes) {

在这种情况下,它甚至会缩短几个字符。另一方面,在你的情况下,你似乎不需要它,所以你可能更喜欢留在较弱的回报类型 RecordReader<NullWritable, LegionRecord> .

展开查看全部
llycmphe

llycmphe2#

正如文章标题所示,我被告知“无法示例化t类型”。从其他堆栈交换文章中,我发现这是不可能的,因为泛型是如何工作的。
这是因为java中的泛型纯粹是编译时特性;编译器丢弃泛型(这称为“类型擦除”),这样在运行时就没有类型变量了 T ,所以你不能这样做 new T(...) .
在java中,可以通过传递 Class<T> 对象创建的示例的方法 T ,然后通过反射创建示例。

相关问题