java中的方法求和
在继续阅读实际文章之前,我想感谢令人敬畏的Javaslang库的作者Daniel Dietrich ,他在我面前有了这个主意:
@lukaseder尝试使用静态方法<T,T1扩展T,... Tn扩展T> Seq <T> toSeq(T1 t1,…,Tn tn){…}(从我的手机中……)
— Daniel Dietrich(@danieldietrich) 2016年2月16日
逆变通用界
这一切都始于一条推文:
您是否不想用Java编写<T super T1&T2&…&TN>?
— Lukas Eder(@lukaseder) 2016年2月16日
我想做一些事情,像模式匹配一组类型的一个普通的超级类型,方法如下:
<T super T1 | T2 | ... | TN>
请注意,我真正想要的是对联合类型的支持,而不是我最初声称的交集类型。
我为什么要这样做? 因为它将很好地添加到jOOλ库中 ,该库具有Java的类型安全元组 :
class Tuple3<T1, T2, T3> {final T1 v1;final T2 v2;final T3 v3;// Lots of useful stuff here
}
在元组中最好的是迭代所有属性的forEach()
方法:
tuple(1, "a", null).forEach(System.out::println);
上面将简单地产生:
1
"a"
null
现在,此forEach()
方法的参数类型是什么? 它看起来像这样:
class Tuple3<T1, T2, T3> {void forEach(Consumer<? super T1 | T2 | T3> c) {}
}
消费者将收到类型为T1 或 T2 或 T3的对象。 但是接受前面三种类型的普通超级类型的消费者也可以。 例如,如果我们有:
Tuple2<Integer, Long> tuple = tuple(1, 2L);
tuple.forEach(v->System.out.println(v.doubleValue()));
上面的代码可以编译,因为Number
是Integer
和Long
的常见超类型,并且它包含doubleValue()
方法。
不幸的是,这在Java中是不可能的
Java当前仅对异常捕获块支持联合/求和类型( 另请参见代数数据类型 ),您可以在其中编写以下内容:
class X extends RuntimeException {void print() {}
}
class X1 extends X {}
class X2 extends X {}// With the above
try {...
}
catch (X1 | X2 e) {// This compiles for the same reasons!e.print();
}
但是不幸的是,catch块是Java中唯一允许使用求和类型的地方。
这是Daniel巧妙而狡猾的解决方法发挥作用的地方。 我们可以编写一个静态方法来使用泛型执行某种“模式匹配”(如果您斜视),反之亦然:
static <T, T1 extends T, T2 extends T, T3 extends T
>
void forEach(Tuple3<T1, T2, T3> tuple, Consumer<? super T> consumer
) {consumer.accept(tuple.v1);consumer.accept(tuple.v2);consumer.accept(tuple.v3);
}
现在可以安全地使用以上方法来推断T1,T2和T3的公共超级类型:
Tuple2<Integer, Long> t = tuple(1, 2L);
forEach(t, c -> {System.out.println(c.doubleValue());
});
符合预期:
1.0
2.0
这是有道理的,因为泛型类型约束是简单地“相反地”指定的,即当T1 extends T
强制T1 extends T
,强制是T super T1
…
如果你真的很quin眼;-)
据说Daniel在Javaslang即将推出的模式匹配API中使用了此技术。 我们期待看到这一结果!
翻译自: https://www.javacodegeeks.com/2016/02/ingenious-workaround-emulate-sum-types-java.html
java中的方法求和