如何将泛型转换为对象?

时间:2020-07-30 14:22:56

标签: java

下面的代码无法编译,因为无法将Task<T>添加到List<Task<Object>>来解决该问题?

import java.util.ArrayList;
import java.util.List;

public class Tasks {

  private final List<Task<Object>> queue = new ArrayList<>();

  public static abstract class Task<T> {}

  public <T> List<T> process(List<Task<T>> tasks) {
    for (Task<T> task : tasks) queue.add(task); // <== problem here
  }

}

2 个答案:

答案 0 :(得分:1)

想象任务就像List一样:它有一个add(T item)方法。

如果您允许将Task<T>添加到Task<Object>的列表中,则其他代码可以从列表中获取一个类型为Task<Object>的项目,这样您就可以调用add(Object o)方法,让您添加所需的任何内容,因为任何内容都是对象。

因此,如果您添加了Task<String>,则您的任务现在将包含非字符串。

这就是为什么不允许这样做的原因。

现在,大概,您的任务绝对没有机会“添加”东西;没有一种方法将T作为参数,T仅作为学术变量以学术变量的形式出现在“获取”位置:例如,它仅以Task类中的方法的返回类型显示。

不幸的是,Java没有那种使使用起来容易的使用场所差异声明模型。

您有两种常规解决方案:

  1. 'uglycasting':使用强制转换构造的第3种形式进行强制转换,其中您强制转换为具有泛型或原始类型的类型:
Task /* raw */ temp = task;
queue.add(temp); // this will work, but generates a warning.

然后使用@SuppressWarnings。请注意,这确实意味着您正在“选择退出”编译器来检查工作,并且如果Task具有add(T)之类的方法或以后得到的方法,则您将得不到任何保护,最终结果可能是您最终在其中强制转换为零的地方看到ClassCastException错误。

  1. 重新定义queue

或者,重新定义队列以接受协方差。从这个意义上讲,Task<Object>毫无用处。实际上,您几乎不会分配的任务类型几乎可以分配给该类型。尝试使用Task<? extends Object>,可以将其缩短为Task<?>。就像使用List<? extends Whatever>一样,您根本无法在这种情况下调用add()样式方法 Task带有T作为参数的任何方法现在都无法调用*。这是好事;这样,您就可以腾出分配任何Task<T>的机会,而不管它可能是什么T。

List<Task<?>> queue = new ArrayList<>();

*)除非您逐字传递null,因为它是每种类型,因此也适合未知范围,但这当然没什么用。

答案 1 :(得分:1)

如果您要存储混合任务类型,则需要使用?,因为您的列表不知道将要传递给哪种类型的Task

private final List<Task<?>> queue = new ArrayList<>();

import java.util.*;
import java.util.stream.Collectors;

public class Tasks {
    // Static classes, methods and fields

    public static abstract class Task<T> {
        private T value;
        public T getValue() { return value; }
        protected Task(T value) { this.value = value; }
        public String toString() { return String.format("Task[value=%s]", this.value);  }
    }

    // Instance fields

    private final List<Task<?>> queue = new ArrayList<>();

    // Class methods

    public List<?> process(List<Task<?>> tasks) {
        for (Task<?> task : tasks) queue.add(task);
        return queue.stream().map(Task::getValue).collect(Collectors.toList());
    }

    // Implementation

    private static class StringTask extends Task<String> {
        protected StringTask(String value) {
            super(value);
        }
    }

    private static class IntTask extends Task<Integer> {
        protected IntTask(Integer value) {
            super(value);
        }
    }

    public static void main(String[] args) {
        Tasks tasks = new Tasks();

        List<Task<?>> taskList = Arrays.asList(
            new StringTask("Hello World"),
            new IntTask(42)
        );

        tasks.process(taskList).stream().forEach(System.out::println);
    }
}