Java 8的用法(泛型接口,谓词链)
1.泛型接口
我们举个例子,以前来看一下JPA定义的写法:
Specification接口为:
public interface Specification<T> { Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3); }
以前的写法为(定义一个静态内部类实现):
public static Specification<AlarmLog> searchKeyword(final String key, final Date startTime, final Date endTime) { return new Specification<AlarmLog>() { @Override public Predicate toPredicate(Root<AlarmLog> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { List<Predicate> predicates = new ArrayList<Predicate>(); if (StringUtils.isNotEmpty(key)) { predicates.add(criteriaBuilder.or( criteriaBuilder.like(root.<String>get("description"), "%" + key + "%"), criteriaBuilder.like(root.<String>get("exceptionMessage"), "%" + key + "%"))); } if (startTime != null && endTime != null){ predicates.add(criteriaBuilder.and( criteriaBuilder.greaterThan(root.<Date>get("createTime"), startTime), criteriaBuilder.lessThan(root.<Date>get("createTime"), endTime) )); } return criteriaBuilder.and(predicates.toArray(new Predicate[]{})); } }; }
1.8以后的写法为:
public static Specification<RecorderTask> searchKeyword(final String key, final Boolean isDeleted) { return (root, criteriaQuery, criteriaBuilder) -> { List<Predicate> predicates = new ArrayList<>(); if (StringUtils.isNotEmpty(key)) { predicates.add(criteriaBuilder.or( criteriaBuilder.like(root.<String>get("fileName"), "%" + key + "%"), criteriaBuilder.like(root.<String>get("outputPath"), "%" + key + "%"))); } if (isDeleted != null) { predicates.add(criteriaBuilder.or( criteriaBuilder.equal(root.<Boolean>get("isDeleted"), isDeleted))); } return criteriaBuilder.and(predicates.toArray(new Predicate[]{})); }; }
2.谓词链(讨论在Java 8中链接Predicates的不同方法):
首先,让我们看看如何使用简单的谓词来过滤名称列表:
@Test public void whenFilterList_thenSuccess(){ List<String> names = Arrays.asList("Adam", "Alexander", "John", "Tom"); List<String> result = names.stream() .filter(name -> name.startsWith("A")) .collect(Collectors.toList()); assertEquals(2, result.size()); assertThat(result, contains("Adam","Alexander")); }
在这个例子中,我们过滤了名称列表,只使用谓词保留以“A”开头的名称:
-
name -> name.startsWith("A")
但是,如果我们想要应用多个Predicates呢?
3.多个过滤器
-
如果我们想要应用多个谓词,一个选项是简单地链接多个过滤器:
-
@Test public void whenFilterListWithMultipleFilters_thenSuccess(){ List<String> result = names.stream() .filter(name -> name.startsWith("A")) .filter(name -> name.length() < 5) .collect(Collectors.toList()); assertEquals(1, result.size()); assertThat(result, contains("Adam")); }
我们现在更新了我们的示例,通过提取以“A”开头并且长度小于5的名称来过滤我们的列表,我们使用了两种过滤修饰-每个谓词。
4.复杂的谓词
现在,我们可以使用一个带有复杂Predicate的过滤器,而不是使用多个过滤器:
@Test public void whenFilterListWithComplexPredicate_thenSuccess(){ List<String> result = names.stream() .filter(name -> name.startsWith("A") && name.length() < 5) .collect(Collectors.toList()); assertEquals(1, result.size()); assertThat(result, contains("Adam")); }
这个选项比第一个选项更灵活,因为我们可以使用按位运算来构建 我们想要的复杂谓词。
5.结合谓词
接下来,如果我们不想使用按位运算构建复杂的谓词,Java 8 Predicate可以使用有用的方法来组合谓词我们将使用Predicate.and(),Predicate.or()和Predicate.negate()方法组合谓词。
5.1 Predicate.and()
在这个例子中,我们将明确定义我们的谓词,然后我们将使用Predicate.and()组合它们:
@Test public void whenFilterListWithCombinedPredicatesUsingAnd_thenSuccess(){ Predicate<String> predicate1 = str -> str.startsWith("A"); Predicate<String> predicate2 = str -> str.length() < 5; List<String> result = names.stream() .filter(predicate1.and(predicate2)) .collect(Collectors.toList()); assertEquals(1, result.size()); assertThat(result, contains("Adam")); }
我们可以看到,语法非常直观,方法名称表明了操作的类型。使用Predicate.and(),我们通过仅提取满足两个条件的名称来过滤我们的列表。
5.2 Predicate.or()
我们也可以使用 Predicate.or()来组合Predicates。 让我们提取名称以“J”开头,以及长度小于4的名称:
@Test public void whenFilterListWithCombinedPredicatesUsingOr_thenSuccess(){ Predicate<String> predicate1 = str -> str.startsWith("J"); Predicate<String> predicate2 = str -> str.length() < 4; List<String> result = names.stream() .filter(predicate1.or(predicate2)) .collect(Collectors.toList()); assertEquals(2, result.size()); assertThat(result, contains("John","Tom")); }
5.3 Predicate.negate()
在组合我们的Predicates时我们也可以使用Predicate.negate():
@Test public void whenFilterListWithCombinedPredicatesUsingOrAndNegate_thenSuccess(){ Predicate<String> predicate1 = str -> str.startsWith("J"); Predicate<String> predicate2 = str -> str.length() < 4; List<String> result = names.stream() .filter(predicate1.or(predicate2.negate())) .collect(Collectors.toList()); assertEquals(3, result.size()); assertThat(result, contains("Adam","Alexander","John")); }
在这里,我们使用or()和negate()的组合来按名称以“J”开头或长度不小于4 来过滤List
5.4 结合谓词内联
我们不需要明确定义要使用的谓词and(), or(),以及negate()。 我们也可以通过强制谓词来内联它们:
@Test public void whenFilterListWithCombinedPredicatesInline_thenSuccess(){ List<String> result = names.stream() .filter(((Predicate<String>)name -> name.startsWith("A")) .and(name -> name.length()<5)) .collect(Collectors.toList()); assertEquals(1, result.size()); assertThat(result, contains("Adam")); }
6.结合一组谓词
最后,让我们看看如何通过减少它们来链接一组Predicates。在下面的例子中,我们有一个列表的谓词,我们使用组合Predicate.and():
@Test public void whenFilterListWithCollectionOfPredicatesUsingAnd_thenSuccess(){ List<Predicate<String>> allPredicates = new ArrayList<Predicate<String>>(); allPredicates.add(str -> str.startsWith("A")); allPredicates.add(str -> str.contains("d")); allPredicates.add(str -> str.length() > 4); List<String> result = names.stream() .filter(allPredicates.stream().reduce(x->true, Predicate::and)) .collect(Collectors.toList()); assertEquals(1, result.size()); assertThat(result, contains("Alexander")); }
注意,我们使用基本标识作为:
-
x->true
但是如果我们想要使用Predicate.or()组合它们会有所不同:
@Test public void whenFilterListWithCollectionOfPredicatesUsingOr_thenSuccess(){ List<String> result = names.stream() .filter(allPredicates.stream().reduce(x->false, Predicate::or)) .collect(Collectors.toList()); assertEquals(2, result.size()); assertThat(result, contains("Adam","Alexander")); }