什么是谓词?什么是行为参数化?

这篇具有很好参考价值的文章主要介绍了什么是谓词?什么是行为参数化?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

java8增加了把方法(你的代码)作为参数传递给另一个方法的能力

背景:应对不断变化的需求

筛选绿苹果

public static List<Apple> filterGreenApples(List<Apple> inventory){
    List<Apple> result = new ArrayList();
    for(Apple apple:inventory){
        if("green".equals(apple.getColor())){
            result.add(apple);
        }
    }
}

如果想要筛选多种颜色:浅绿色、暗红色、黄色等,这种方法就应付不了了。一个良好的原则是在编写类似的代码之后,尝试将其抽象化

将颜色作为参数

一种做法是给方法加一个参数,把颜色变成参数,这样就能灵活地适应变化了:

public static List<Apple> filterGreenApples(List<Apple> inventory,String color){
    List<Apple> result = new ArrayList();
    for(Apple apple:inventory){
        if(color.equals(apple.getColor())){
            result.add(apple);
        }
    }
}

如果要能区分轻的苹果和重的苹果怎么办?例如筛选出重量大于150g的苹果?用另一个参数应对?

public static List<Apple> filterGreenApples(List<Apple> inventory,int weight){
    List<Apple> result = new ArrayList();
    for(Apple apple:inventory){
        if(color.getWeight() > weight){
            result.add(apple);
        }
    }
}

以上代码有哪些问题?

  1. 打破DRY(Don’t Repeat Yourself)
  2. 修改筛选方式就意味着要修改所有方法的实现
  3. 绝对不要加上一个标志来区分对颜色和重量的查询

行为参数化

现在需要一种比添加很多参数更好的方法来应对变化的需求

一种好的方案是对你的选择标准建模,需要根据Apple的某些属性来返回一个boolean值,我们把它称为谓词(即一个返回boolean值的函数)。

我们来定义一个接口来对选择标准建模:

public interface ApplePredicate{
    boolean test(Apple apple);
}

现在可以使用ApplePredicate的多个实现代表不同的选择标准了,比如:

public class AppleHeavyWeightPredicate implements ApplePredicate{
    public boolean test(Apple apple){
        return apple.getWight()>150;
    }
}
public class AppleGreenColorPredicate implements ApplePredicate{
    public boolean test(Apple apple){
        return "green".equals(apple.getColor());
    }
}

可以将这些标准看做filter方法的不同行为。如上是一个策略设计模式,该怎么利用ApplePredicate的不同实现呢?需要filterApples方法接受ApplePredicate对象,对Apple做条件测试,这就是参数行为化:让方法接受多种行为(或战略)作为参数,并在内部使用,来完成不同的行为

根据抽象条件筛选

利用ApplePredicate改过之后,filter方法如下:

public static List<Apple> filterApples(List<Apple> inventory,ApplePredicate p){
    List<Apple> result = new ArrayList();
    for(Apple apple:inventory){
        if(p.test(apple)){
            result.add(apple);
        }
    }
    return result;
}

传递代码行为

想在可以创建不同的ApplePredicate对象,并将他们传递给filterApples方法。比如如果让你找出所有重量超过150g的红苹果,只需要创建一个类来实现ApplePredicate就可以了。现在的代码足够灵活,可以应对任何涉及苹果属性的需求变更了:

public class AppleRedAndHeavyPredicate implements ApplePredicate{
    public boolean test(Apple apple){
        return "red".equals(apple.getColor()) && apple.getWeight()>150;
    }
}

List<Apple> redAndHeavyApples = filterApples(inventory,new AppleRedAndHeavyPredicate());

这已经完成了一件很酷的事:filterApples方法的行为取决于你通过ApplePredicate对象传递的代码,换句话说,已经将filterApples方法的行为参数化了

上面的例子中,重要的代码是test方法的实现,正是它定义了filterApples方法的新行为。令人遗憾的是,由于该filterApples方法只能接受对象,所以你必须把代码包裹在ApplePredicate对象里。你的做法就类似于在内联“传递代码”,因为你是通过一个实现了test方法的对象来传递布尔表达式的。

通过使用lambda,你可以直接把表达式(“red”.equals(apple.getColor()) && apple.getWeight()>150)传递给filterApples方法,而无需定义多个ApplePredicate类,从而去掉不必要的代码。

参数化filterApples的行为,传递不同的筛选策略(ApplePredicate p)

多种行为,一个参数

行为参数化的好处在于你可以把迭代要筛选的集合的逻辑与对集合中每个元素应用的行为区分开来。这样可以重复使用同一个方法,给它不同的行为来达到不同的目的。

对付啰嗦

目前,当要把新的行为传递给filterApples方法的时候,不得不声明好几个实现ApplePredicate接口的类,然后实例化好几个只会提到一次的ApplePredicate对象。

费这么大劲真没必要,如何做的更好呢?如何同时声明和实例化一个类?

匿名类

匿名类和java局部类(块中定义的类)差不多,但匿名类没有名字。它允许你同时声明并实例化一个类。换句话说,它允许你随用随建。

使用匿名类

List<Apple> redApple = filterApples(inventory,new ApplePredicate(){
    //直接内联参数化filterApples方法的行为
    public boolean test(Apple apple){
        return "red".equals(apple.getColor());
    }
});

但匿名类还是不够好:

  • 它往往很笨重,因为它占用了很多空间。
  • 很多程序员觉得它用起来很让人费解。

即使匿名类处理在某种程度上改善了为一个接口声明好几个实体类的啰嗦问题,但它仍不能令人满意。

在只需要传递一段简单的代码时(例如选择标准的boolean表达式),你还是要创建一个对象,明确地实现一个方法来定义一个新的行为。

Lambda表达式

上面的代码可以使用Lambda表达式重写为:

List<Apple> result = filterApples(inventory,(Apple apple) -> "red".equals(apple.getColor()));

这段代码看上去比之前干净多了,因为它看起来更像问题陈述本身了。

将List类型抽象化

在通往抽象的路上,我们还可以更进一步,目前,filterApples方法还只适用于Apple。还可以将List类型抽象化,从而超越眼前要处理的问题:

public interface Predicate<T>{
    boolean test(T t);
}

pulic static <T> List<T> filter(List<T> list,Predicate<T> P){
    List<T> result = new ArrayList();
    for(T e:list){
        if(p.test(e)){
            result.add(e);
        }
    }
    return result;
}

现在可以把filter方法用在香蕉、桔子、Integer或者String的列表上了,如下所示:

List<Apple> redApples = filter(inventory,(Apple apple) -> "red".equals(apple.getColor()));

List<Integer> evenNumbers = filter(inventory,(Integer i) -> i % 2 == 0);

现在在灵活性和简洁性之间找到了最佳平衡点,是不是很酷?

实战

用Comparator来排序

对集合排序是一个常见的编程任务。我们可以使用一种方法来表示和使用不同的排序行为,来轻松地适应变化的需求。

在java8中List自带了一个sort方法(也可以使用Collections.sort)。sort的行为可以用java.util.Comparator对象来参数化,它的接口如下:

public interface Comparator<T>{
    public int compare(T o1,T o2);
}

用Lambda表达式:

inventory.sort((Apple a1,Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

用Runnable执行代码块

线程就像是轻量级的进程:它们自己执行一个代码块,可以使用Runnbale接口表示一个要执行的代码块(void代表代码不会返回任何结果)。

public interface Runnable{
    public void run();
}

使用这个接口创建执行不同行为的线程:

Thread t = new Thread(new Runnable(){
    public void run(){
        System.out.println("Hello world");
    }
});

用Lambda表达式:文章来源地址https://www.toymoban.com/news/detail-415295.html

Thread t = new Thread(() -> System.out.println("Hello world"));

总结

  • 行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力
  • 行为参数化可让代码更好地适应不断变化的要求,减轻未来的工作量
  • 传递代码,就是将新行为作为参数传递给方法。
  • Java API包含很多可以用不同行为进行参数化的方法,包括排序、线程等。

到了这里,关于什么是谓词?什么是行为参数化?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java8 Stream流常用方法(持续更新中...)

    操作对象 模拟数据 操作 打印结果 打印结果 注意:异常自己捕捉,就比如这里String转Intger就可能出现NumberFormatException异常 打印结果 打印结果 断点查看 打印结果 断点查看 持续更新中…

    2024年04月28日
    浏览(60)
  • Java8 中map中删除元素的简单方法

    2024年04月23日
    浏览(28)
  • Java8函数式接口, 方法引用, 构造器引用, 数组引用

    只包含一个抽象方法的接口,称为函数式接口。 你可以通过Lambda表达式来创建该接口的对象。(若Lambda表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明 我们可以在一个接口上使用 @Functionallnterface 注解,这样做可以检查它是

    2024年02月05日
    浏览(56)
  • 使用Java8 Stream流中的Collectors.collectingAndThen()方法去重

    Collectors.collectingAndThen() 根据对象属性进行去重操作 Collectors.collectingAndThen()方法属于java8 Stream流中的 java.util.stream.Collectors ,此类实现了 java.util.stream.Collector 接口,还提供了大量的方法对Stream流中的元素进行 map 和 reduce 操作 在获取任务的时候,会出现id重复的状况,利用 Co

    2024年02月09日
    浏览(59)
  • java8 Instant 计算方法耗时, 再见了我的System.currentTimeMillis()

    以下是一个 Java Instant 计算方法耗时的示例代码: 在上述示例代码中,我们使用 Instant.now() 方法获取当前时间,并在执行需要计算耗时的方法前后分别获取当前时间,然后使用 Duration.between() 方法计算两个时间点之间的时间差,并将结果输出到控制台。 在示例代码中,我们使

    2023年04月10日
    浏览(41)
  • java8 列表通过 stream流 根据对象属性去重的三种实现方法

    0、User对象 1、使用filter进行去重 测试 ①、疑惑 既然 filter 里面调用的是 distinctPredicate 方法,而该方法每次都 new 一个新的 map 对象,那么 map 就是新的,怎么能做到可以过滤呢 ②、解惑 先看一下 filter 的部分实现逻辑,他使用了函数式接口 Predicate ,每次调用filter时,会使用

    2024年01月20日
    浏览(95)
  • JAVA8-lambda表达式8:在设计模式-模板方法中的应用

    JAVA8-lambda表达式1:什么是lambda表达式 JAVA8-lambda表达式2:常用的集合类api JAVA8-lambda表达式3:并行流,提升效率的利器? JAVA8-lambda表达式4:Optional用法 java8-lambda表达式5:toMap引发的线上故障 JAVA8-lambda表达式6:重构和定制收集器 JAVA8-lambda表达式7:重要的函数接口 最近在公司

    2024年02月14日
    浏览(49)
  • java8利用Stream方法求两个List对象的交集、差集与并集(即:anyMatch和allMatch和noneMatch的区别详解)

    1、anyMatch  判断数据列表中是否存在任意一个元素符合设置的predicate条件,如果是就返回true,否则返回false。 接口定义: boolean anyMatch(Predicate? super T predicate); 方法描述: 在anyMatch 接口定义中是接收 Predicate 类型参数,在Lamdba表达式中 PredicateT 是接收一个T类型参数,然后经过

    2024年02月06日
    浏览(54)
  • 【Java 基础篇】Java可变参数:灵活处理不定数量的方法参数

    在Java编程中,可变参数是一项强大的功能,它允许你编写更加灵活的方法,接受不定数量的参数。本文将详细解释Java可变参数的用法、语法以及最佳实践。 可变参数是Java 5引入的一项功能,它允许你在方法中传递不定数量的参数。可变参数用三个点( ... )表示,放置在方

    2024年04月17日
    浏览(41)
  • Java Spring 通过 AOP 实现方法参数的重新赋值、修改方法参数的取值

    我创建的项目项目为 SpringBoot 项目 这里以对前端传递过来的加密数据进行解密为例 注解 控制器方法 方式一:通过环绕通知实现 [个人比较推荐] 方式二:通过前置通知 + 反射实现 Java ReflectUtil 反射相关的工具类 由于 JDK 8 中有关反射相关的功能自从 JDK 9 开始就已经被限制了

    2024年02月04日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包