【发布时间】:2014-04-11 21:31:20
【问题描述】:
我今天偶然发现了一个小问题。考虑一个小包装类:
class Event<T> {
T value;
Class<T> type;
// other fields, getters and setters omitted for brevity
}
现在我想将Event<Long> 转换为Event<String>,同时保留其他字段并更新type 成员。
最终我得到了最简单的“解决方案”:
Event<String> new = new Event(old.getValue().toString(), String.class, other, fields);
然而,在我的宠物项目中与 Haskell 合作后,我自然渴望像 fmap :: Functor f => (a -> b) -> f a -> f b 这样的函数(阅读:给定一个从 a 到 b 的函数和一个包含 a 类型的函子,给我一个包含 b 的结果)和在发现没有标准实现后,我开始自己编写一个:
interface Functor<T> {
Functor<S> fmap( Func1<T,S> f );
}
// ... in Event<T>:
Functor<S> fmap( Func1<T,S> f ) {
S newValue = f.call(this.value);
return new Event( newValue, newValue.getClass(), other, fields);
}
现在这个解决方案有一个问题:在 Java 中调用 fmap 之后,我留下了一个 Functor<String> 类型的实例,而 Haskell 中的相同函数将返回一个 Event<String>。
有没有办法让我的Event 回来(不会不安全地投射它)?
【问题讨论】:
-
这里需要更高种类的多态性。您需要对 S 和 T 变量以及称为 Functor 的部分进行抽象。
-
Java 的类型系统不能真正代表更高种类的类型构造函数。
-
将
Event类中fmap的返回类型改为Event<S>。由于Event是Functor的子类,这应该可以工作。