class Event<T> {
T value;
Class<T> type;
// other fields, getters and setters omitted for brevity
}
现在我处于这样一种情况:我希望将Event<Long>
转换为Event<String>
,同时保留其他字段并更新type
成员。
最终我得到了最简单的解决方案&#34;:
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
回来(没有不安全地投射它)?
答案 0 :(得分:8)
不,这是不可能的。要实现这一点,我们需要在您的界面中对Functor
进行抽象,例如
interface Functor<T> as F<T> {
F<S> map(f : Function<T, S>);
}
但Java不允许您通过类型构造函数进行抽象,只需类型。这被称为高级金属类型(HKT)。只有少数(非依赖)语言有HKT,Scala和Haskell是我能想到的唯一两种语言。
事实上HKT是表达大量抽象的必要条件,
category-extras
所有这些都涉及抽象类型构造函数,而不仅仅是具体类型。
答案 1 :(得分:3)
这对我来说相当不错,但它并不完全Functor<T>
。它还要求您指定F
,即仿函数为实例的类型:
interface Fn1<A, B> {
B apply(A a);
}
interface Functor<A, F extends Functor<?, ?>> {
<B> F map(Fn1<A, B> f);
}
您的Event<A>
课程将实现Functor
,如下所示:
public class Event<A> implements Functor<A, Event<?>> {
public final A value;
public Event(A _value) {
value = _value;
}
public <B> Event<B> map(Fn1<A, B> f) {
return new Event<B>(f.apply(value));
}
public String toString() {
return "Event<" + value.getClass().getSimpleName() + ">(" + value.toString() + ")";
}
}
如果你需要一个封闭式扩展类的仿函数(例如来自rxjava的Observable
),你可以编写一个看起来更像类型类的仿函数,但它不能符合任何类似仿函数的接口,因为Java缺少更高级的类型:
public class ObservableFunctor {
public static <A,B> Observable<B> map(Observable<A> fa, Fn1<A, B> f) {
return new Observable<B>(f.apply(fa.value));
}
}
以下是一个可运行的示例,它使用了上面的Event
和Observable
:
public class FunctorDemo {
interface Fn1<A, B> {
B apply(A a);
}
interface Functor<A, F extends Functor<?, ?>> {
<B> F map(Fn1<A, B> f);
}
static class ObservableFunctor {
public static <A,B> Observable<B> map(Observable<A> fa, Fn1<A, B> f) {
return new Observable<B>(f.apply(fa.value));
}
}
static class Observable<A> {
public final A value;
public Observable(A _value) {
value = _value;
}
public String toString() {
return "Observable<" + value.getClass().getSimpleName() + ">(" + value.toString() + ")";
}
}
static class Event<A> implements Functor<A, Event<?>> {
public final A value;
public Event(A _value) {
value = _value;
}
public <B> Event<B> map(Fn1<A, B> f) {
return new Event<B>(f.apply(value));
}
public String toString() {
return "Event<" + value.getClass().getSimpleName() + ">(" + value.toString() + ")";
}
}
public static void main(String[] args) {
Observable<Event<Long>> oe1 = new Observable(new Event(42L));
System.out.println("oe1: " + oe1.toString()); // oe1: Observable<Event>(Event<Long>(42))
Observable<Event<String>> oe2 = ObservableFunctor.map(oe1,
new Fn1<Event<Long>, Event<String>>() {
public Event<String> apply(Event<Long> e) {
return e.map(
new Fn1<Long, String>() {
public String apply(Long l) {
return l.toString();
}
}
);
}
}
);
System.out.println("oe2: " + oe2.toString()); // oe2: Observable<Event>(Event<String>(42))
}
}