英文:
How to cast generic type to Object?
问题
以下是您要翻译的部分:
"The code below won't compile because it can't add Task<T>
to List<Task<Object>>
how to fix it?"
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
}
}
英文:
The code below won't compile because it can't add Task<T>
to List<Task<Object>>
how to fix it?
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
}
}
答案1
得分: 1
想象一下,任务(Task)就像是一个List
(列表):它有一个add(T item)
(添加项目)方法。
如果允许将Task<T>
添加到Task<Object>
列表中,那么其他代码就可以从列表中获取一个项目,该项目的类型将为Task<Object>
,因此您可以调用add(Object o)
方法,从而可以添加任何您想要的内容,因为任何东西都是一个对象。
所以,如果您添加了一个Task<String>
,那么您的任务现在就包含了非字符串的内容。
这就是为什么不允许这样做的原因。
现在,很可能您的任务根本没有机会来“添加”事物;所有的方法都没有T作为参数,T仅出现在“get”位置,从学术角度来看,它是一个协变选项:例如,它仅出现在Task
类的方法返回类型中。
不幸的是,Java不具备一种方便的使用地点协变声明模型。
您有两个一般解决方案:
- 'uglycasting':进行强制类型转换,使用强制类型转换结构的第三种形式,其中您将类型转换为具有泛型的类型,或者可能是一个原始类型:
Task /* 原始 */ temp = task;
queue.add(temp); // 这将起作用,但会生成警告。
然后使用@SuppressWarnings
。请注意,这意味着您正在“退出”编译器检查您的工作,如果Task
具有一个类似于add(T)
的方法或者以后添加了一个,您将没有保护,并且最终结果很可能是在其中没有任何强制转换的地方看到ClassCastException
错误。
- 重新定义
queue
。
或者,重新定义queue
以接受协变性。在这种意义上,Task<Object>
相当无用;您制作的任务类型很少或根本不可能分配给这种类型。尝试Task<? extends Object>
,它可以缩写为Task<?>
。就像List<? extends Whatever>
一样,您无法在这种情况下调用add()
样式的方法;Task
具有的任何接受T
作为参数的方法现在都不能调用*。这是一件好事;它使您可以自由地将任何Task<T>
分配给它,而不管T是什么。
List<Task<?>> queue = new ArrayList<>();
*) 除非您传递字面上的null
,这是每种类型,因此也适用于未知边界,但这实际上是无用的。
英文:
Imagine Task was exactly like List
: It has an add(T item)
method.
If you allowed adding a Task<T>
to a List of Task<Object>
, then other code could then grab an item from the list, which would have type Task<Object>
, which would let you thus call the add(Object o)
method, letting you add anything you want, as anything is an object.
So if you added a Task<String>
, your task now has non-strings in it.
That's why this isn't allowed.
Now, presumably, your task has absolutely no opportunities to 'add' things; none of the methods have a T as parameter, the T occurs solely in a 'get' position, in academicese, as a covariant option: For example, it only shows up in return types of methods in the Task
class.
Java unfortunately does not have the kind of use-site variance declaration model that makes this easy.
You have two general solutions:
- 'uglycasting': Cast things, using the 3rd form of the cast construct, where you cast to a type that has generics, or perhaps, a raw type:
Task /* raw */ temp = task;
queue.add(temp); // this will work, but generates a warning.
then use @SuppressWarnings
. Note that this does mean you're 'opting out' of the compiler checking your work, and if Task either has a method like add(T)
or gets one later, you get no protections, and the end result will likely be that you end up seeing ClassCastException
errors in places with zero casts in them.
- Redefine
queue
.
Alternatively, redefinine queue to accept covariance. In that sense, Task<Object>
is rather useless; few to no task types you'll ever make are actually assignable to this type. Try Task<? extends Object>
, which can be shortened to just Task<?>
. Just like with a List<? extends Whatever>
, you cannot invoke add()
style methods at all on such a thing; any method Task
has that takes as parameter a T
are now not invokable*. That's a good thing; it frees you up to assign any Task<T>
regardless of what T might be to it, then.
List<Task<?>> queue = new ArrayList<>();
*) Unless you pass literally null
, which is every type, and thus also fits for an unknown bound, but that's rather useless, of course.
答案2
得分: 1
如果您正在存储混合的任务类型,您需要使用 ?
,因为您的列表不知道将传递什么类型的 Task
。
private final List<Task<?>> queue = new ArrayList<>();
import java.util.*;
import java.util.stream.Collectors;
public class Tasks {
// 静态类、方法和字段
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); }
}
// 实例字段
private final List<Task<?>> queue = new ArrayList<>();
// 类方法
public List<?> process(List<Task<?>> tasks) {
for (Task<?> task : tasks) queue.add(task);
return queue.stream().map(Task::getValue).collect(Collectors.toList());
}
// 实现
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);
}
}
英文:
If you are storing mixed task types, you need to use ?
, since your list does not know what kind of Task
is going to be passed into it.
> 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);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论