【发布时间】:2021-04-26 06:37:26
【问题描述】:
class Undoable<T> {
T value;
Deque<Object> history;
Undoable(T t, Deque<Object> history) {
this.value = t;
this.history = history;
}
static <T> Undoable<T> of(T t) {
return new Undoable<T>(t, new LinkedList<Object>());
}
public <R> Undoable<R> flatMap(Function<T, Undoable<R>> mapper) {
Undoable<R> r = mapper.apply(value);
Deque<Object> newHistory = new LinkedList<>();
newHistory.addAll(history);
newHistory.addAll(r.history);
return new Undoable<R>(r.value, newHistory);
}
public <R> Undoable<R> undo() {
Deque<Object> newHistory = new LinkedList<>(this.history);
R r;
try {
r = (R)newHistory.removeLast(); //line A
} catch (NoSuchElementException e) {
throw new CannotUndoException();
}
return new Undoable<R>(r, newHistory);
}
}
鉴于上面的代码 sn-p,假设我们将 A 行保持原样并忽略编译警告。如果我们会发生什么
Undoable<Integer> i = Undoable.of("hello").flatMap(s -> {
Deque<Object> history;
history = new LinkedList<>();
history.add(s);
return new Undoable<Integer>(s.length(), history);
});
Undoable<Double> d = i.undo();
我知道Undoable<Integer> 和Undoable<Double> 会擦除为Undoable。但是undo() 方法中的R 会变成什么?由于Double 是该方法调用的目标类型,它是否推断为Double?
我试过在我的机器上运行代码,它运行良好,但我不知道为什么。任何人都可以解释一下吗?非常感谢!
【问题讨论】:
-
既然我能理解,是的,它会从 Integer 转换为 Double,尝试用不同的东西声明 d 变量,看看它会抛出一个错误。
-
对,另一个问题是这个问题的更一般的变体。我对类型推断和类型擦除的工作方式感到困惑。最初,我的想法是既然R被推断为Double,为什么不抛出ClassCastException。我怀疑 R 最终被擦除到 Object 但这并没有真正意义,因为如果 R 已经被推断为 Double,为什么 R 会擦除到 Object。既然你和 Wasserman 把事情弄清楚了,我意识到 R 仍然被擦除到 Object 并且在检查它是类型安全之后推理丢失了。
-
很高兴这有帮助,但请用引发强制转换异常的东西对其进行真正的测试,我现在很好奇,尝试将 Double 更改为像 InputStream 之类的荒谬的东西。
-
如果我取出它的值并对其进行操作,我只会得到一个 ClassCastException。对不起,我对 Java 还是很陌生,所以我不知道 InputStream 类型是如何工作的。但是,如果我执行
System.out.println(d.value/2);之类的操作,我将得到一个 ClassCastException。只有当我们对其进行操作时,Java 才会意识到它不匹配。我希望我展示了你在寻找什么哈哈哈。
标签: java generics type-erasure