从零开始的java学习Day16----------基础篇(函数式编程思想、函数式接口)

函数式编程思想

在编程中,除了面向对象思想,还有函数式编程思想。面向对象强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。
Java中Lambda就是一种函数式编程思想的语法,例如我们之前使用的匿名内部类,就可以使用Lambda来替代。

Lambda标准格式:
(参数类型 参数名称) -> { 代码语句 }
格式说明:
  • 小括号内的语法与传统方法(抽象方法)参数列表一致:无参数则留空;多个参数则用逗号分隔
  • ->是新引入的语法格式,代表指向动作
  • 大括号内的语法与传统方法体要求基本一致
Lambda省略格式(在标准格式的基础上):
  • 参数类型可以省略
  • 如果小括号内的参数有且只有一个,小括号可以省略
  • 如果大括号内只有一个语句,无论是否有返回值,都可以省略(大括号、retunrn、分号)
    示例1(无参):
    //先定义一个接口,里面只有一个抽象方法
public interface JieK {
  void a();
}
//在主方法中调用
public class Demo2 {
  public static void main(String[] args) {
//这里a就是下面定义的方法,因为a需要传入JieK接口的时限类对象,所以可以使用Lambda
  	a(()->System.out.println("呵呵"));  //运行程序会输入"呵呵"
  }

//这里需要新建一个方法,这个方法需要传入的就上上面的接口
  static void a(JieK j){
  	j.a();
  }
}

示例2(有参):
用Lambda写comparator的比较器实现类方法(降序)

Integer[] arr ={2,5,7,8,9,1,3};
Arrays.sort((o1,o2)->o2-o1);
此时,arr数组里的数字,已经按照降序排序了

小结

  • Lambda只能作为代替要传入的一个接口的实现对象时使用,且该接口只有一个需要重写的方法
  • 当方法的形参,需要传入的是一个接口,且该接口有且只有一个抽象方法,才可以使用Lambda

函数式接口

函数式接口在java中指的就是有且仅有一个抽象方法的接口。(必须有一个抽象方法,其他方法或变量可以随意)
函数式接口,可以适用于函数式编程的接口,在Java中函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda的接口。

如何判断

判断一个接口是不是函数式接口,可以用@FunctionalInterface标识,能被它标识的接口就是函数式接口

使用场景

函数式接口的典型应用场景就是作为方法的参数和返回值

Lambda的延迟执行

我们在传递参数时,如果参数类型是函数式接口,等实际调用该方法时,我们用Lambda表达式当作这个接口对象传入进去,穿进去的时候,Lambda表达式不会立即执行,需要等到该方法调用函数式接口的方法时才会执行,这样就有避免掉了一些资源浪费(比如该参数实际不会被用到)

方法引用

在使用Lambda表达式的时候,我们实际上传进去的代码就是一种解决方案。那么考虑到一种情况,我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那就没有必要再写重复逻辑了

方法引用符

双冒号::为引用运算符,而他所在的表达式被称为方法引用,是一种Lambda表达式的改进写法。条件是,Lambda表达式的函数方案已经存在于某个方法中,那么可以通过双冒号来引用该方法为Lambda的替代者
下面是几种使用格式

通过对象名引用成员方法

这是最常见的一种用法,当一个类中有某个方法是我们需要的函数方案,可以通过这个类的对象来引用
格式:

对象名::方法名
通过类名引用静态方法

常用于一些系统里已经有的类的静态方法,可以直接通过类名来引用
格式:

类名::方法名
例:Math::abs               //abs是Math类的静态方法
通过super引用父类方法

当我们操作的类存在继承关系,我们在使用Lambda时,需要引用父类中的方法,可以使用super调用
格式:

super::方法名
通过this引用成员方法

当我们操作的时候,想要引用自己类中的方法,可以使用this调用
格式:

this::方法名
类的构造器的引用

当我们使用Lambda时,需要实现的功能是要创建某个类的对象,就可以直接引用该类的构造方法来创建
格式:

类名::new
数组构造器的引用

同样的,当我们想要使用Lambda完成一个创建数组的功能时,也可以直接引用数组的构造器,注意,数组的构造器是需要传入长度(所以需要原使用的Lambda有int参数)
格式:

数组类型[]::new

常用函数接式口

JDK提供了大量的常用函数式接口以丰富Lambda的典型使用场景,他们主要在java.util.function包中被提供。下面是最简单的几个接口及使用示例

Supplier接口

该接口负责生产数据(无参数,有返回值),又叫做生产接口
Supplier该接口仅包含一个无参的方法:T get(),通过作为形参时指定数据类型,执行方法,返回一个对应的数据类型对象。
示例(求数组的最大值)

public static void main(String[] args) {
    int[] intArr =new int[]{1,3,4,5,6,22,3,4,19,-9};
    maxarr(() -> {
        int max = intArr[0];
        for (int i : intArr)
            if (i > max)
                max = i;
        return max;
    });
}
static public int maxarr(Supplier<Integer> max){
    return max.get();
}

该接口的方法只负责返回一个对应类型的对象,内部的方法还是要自己去书写

Consumer接口

Consumer此接口与Supplier 接口刚好相反,它负责消费指定类型数据(有参数,无返回值)
接口中包含一个抽象方法void accept(T t),意为消费一个指定泛型的数据
示例(输出打印传入的对象):

public static void main(String[] args) {
    XiaoFei((str)->{
        System.out.println(str);
    },"呵呵");
}
public static void XiaoFei(Consumer<String> consu,String str){
    consu.accept(str);
}
Consumer接口中还包含一个默认方法andThen,该方法可以实现一个数据消费两次

示例(输出打印传入的对象两次):

public static void main(String[] args) {
    XiaoFei("呵呵",(str)->{
        System.out.println(str);
    },(str)->{
        System.out.println(str+"1");
    });
}
public static void XiaoFei(String str,Consumer<String> con1,Consumer<String> con2){
    con1.andThen(con2).accept(str);     //这里哪个con对象写在前面,就会先执行哪个对象对应的Lambda内的方法
}

Predicate接口

Predicate该接口内的方法是能一个能得到boolean值的结果,我们叫它判断接口,当我们需要对某种类型的数据进行判断就可以使用
boolean test(T t)方法,判断传入的数据,返回一个布尔值
示例:

  public static void main(String[] args) {
  String st = "cdcda";
// 判断字符串是否包含"dd"
  boolean res = pand1(st, xx -> xx.contains("dd"));
  System.out.println(res);
  }

//对一个字符串进行判断
  static boolean pand1(String str, Predicate<String> a) {
  return a.test(str);
  }
Predicate接口内包含三个默认方法,分别是and,or和negate

and和or,逻辑上的"与"和"或"
当我们要对同一个数据进行多次比较时可以使用,可以链式编程,执行流程是从左往右
and等价&&符号
or等价||符号
格式示例:

    public static void main(String[] args) {
//        判断字符串是否包含"a"和"cn",或者包含"c"
        String st = "ab";//用它执行下列方法为true
        String st1 = "c";//用它执行下列方法也为true
        boolean res = pand1(st, xx -> xx.contains("a"),xx -> xx.contains("b"),
                xx -> xx.contains("c"));
        System.out.println(res);
    }

//对一个字符串进行3次判断
    static boolean pand1(String str, Predicate<String> a,Predicate<String> b,
                         Predicate<String> c) {
//先执行a.test(str)&&b.test(str)的结果,然后拿其值 ||c.test(str)的结果
        return a.and(b).or(c).test(str);
    }

nagate,逻辑取反,可以取反执行了test()后的值,注意,一定要在调用test方法调用它
格式:

nagate.test();等价于!test();
链式编程示例:
//设p1的lambda是判读数是否是偶数,p2的Lambda是判断数是否小于0
p1.nagate.or(p2).test(-1)      //该语句等价于:p1.nagate.test(-1)||p2.test(-1),结果为true

Function接口
Function<T,R>该接口的功能就是通过T,得到一个R类型的数据,我们通常称为转换接口
抽象方法:R apply(T t)
使用示例:

  public static void main(String[] args) {
  Integer i = 100;
  boolean b =true;
  System.out.println(deao(i, a -> a + ""));//输出100字符串
  System.out.println(deao(b, a -> a + ""));//输出ture字符串

  }
// 将任意数据转换成String
  static <T> String deao(T a ,Function<T,String> c){
  String a1 = c.apply(a);
  return a1;
  }

Function接口也内含默认方法andThen,和Consumer内的差不多,都是先执行前面的,再执行后面的,只是它里面的andThen,第二次操作的数是第一次返回的值
使用示例:

public static void main(String[] args) {
    boolean flag =true;
    char[] chars = deao2(flag, a -> a + "", b -> b.toCharArray());
    for (char c : chars) {
        System.out.println(c);
    }
}
//    将任意数据转换成String,再转为字符数组
static <T,R> char[] deao2(T a ,Function<T,R> c,Function<R,char[]> d){
    return c.andThen(d).apply(a);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值