在 Java 8 中可以将模式匹配实现为一个库(利用 lambda 表达式),但不幸的是,我们仍然会缺少 Haskell 或 Scala 等语言所具有的编译器详尽性检查。
Cyclops-react 有一个强大的 Pattern Matching 模块,它提供 Java 8 的结构模式匹配和通过守卫的模式匹配。
它提供了when/then/otherwise DSL 和匹配,包括基于标准Java Predicates 的解构(例如可以使用匹配来过滤一个Stream)。
守卫匹配
为了通过守卫进行匹配,我们使用 whenGuard / then / else 来清楚地显示案例正在驱动测试,而不是被测对象的结构。
例如对于基于守卫的匹配,如果我们实现一个实现 Matchable 接口的 Case 类
static class MyCase implements Matchable{ int a; int b; int c;}
(顺便说一句,Lombok 可以非常方便地创建密封案例类层次结构)
我们可以匹配它的内部值(必要时递归,或按类型以及其他各种选项)。
import static com.aol.cyclops.control.Matchable.otherwise;
import static com.aol.cyclops.control.Matchable.whenGuard;
new MyCase(1,2,3).matches(c->c.is(whenGuard(1,2,3)).then("hello"),
.is(whenGuard(4,5,6)).then("goodbye")
,otherwise("goodbye")
);
如果我们有一个没有实现 [Matchable][3] 的 Object,无论如何我们都可以强制它为 Matchable,我们的代码会变成
Matchable.ofDecomposable(()->new MyCase(1,2,3)))
.matches(c->c.is(whenGuard(1,2,3)).then("hello"),
.is(whenGuard(4,5,6)).then("goodbye")
,otherwise("hello"));
如果我们不关心其中一个值,我们可以使用通配符
new MyCase(1,2,3).matches(c->c.is(whenGuard(1,__,3)).then("hello"),
.is(whenGuard(4,__,6)).then("goodbye")
,otherwise("hello)
);
或者递归地解构一组嵌套的类
Matchable.of(new NestedCase(1,2,new NestedCase(3,4,null)))
.matches(c->c.is(whenGuard(1,__,has(3,4,__)).then("2")
,otherwise("default");
NestedCase 看起来像这样 -
class NestedCase implemends Decomposable { int a; int b; NestedCase c; }
用户还可以使用 hamcrest 编写模式匹配表达式
import static com.aol.cyclops.control.Matchable.otherwise;
import static com.aol.cyclops.control.Matchable.then;
import static com.aol.cyclops.control.Matchable.when;
Matchable.of(Arrays.asList(1,2,3))
.matches(c->c.is(when(equalTo(1),any(Integer.class),equalTo(4)))
.then("2"),otherwise("default"));
结构模式匹配
我们还可以匹配被测对象的确切结构。这不是使用 if / then 测试来查看结构是否恰好匹配我们的案例,我们可以让编译器确保我们的案例与提供的对象的结构相匹配。执行此操作的 DSL 与基于保护的匹配几乎相同,但我们使用 when / then / else 来清楚地显示对象结构驱动测试用例,反之亦然。
import static com.aol.cyclops.control.Matchable.otherwise;
import static com.aol.cyclops.control.Matchable.then;
import static com.aol.cyclops.control.Matchable.when;
String result = new Customer("test",new Address(10,"hello","my city"))
.match()
.on$_2()
.matches(c->c.is(when(decons(when(10,"hello","my city"))),then("hello")), otherwise("miss")).get();
//"hello"
对从客户中提取的地址对象进行结构匹配。 Customer 和 Address 类看起来是这样的
@AllArgsConstructor
static class Address{
int house;
String street;
String city;
public MTuple3<Integer,String,String> match(){
return Matchable.from(()->house,()->street,()->city);
}
}
@AllArgsConstructor
static class Customer{
String name;
Address address;
public MTuple2<String,MTuple3<Integer,String,String>> match(){
return Matchable.from(()->name,()->Maybe.ofNullable(address).map(a->a.match()).orElseGet(()->null));
}
}
cyclops-react 提供了一个Matchables 类,它允许对常见的JDK 类型进行结构模式匹配。