day21_Lambda表达式、函数式接口

一、Lambda表达式

1.1 函数式编程思想概述

 

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做

1.2 冗余的Runnable代码

传统写法

当需要启动一个线程去完成任务时,通常会通过java.lang.Runnable接口来定义任务内容,并使用java.lang.Thread类来启动该线程。代码如下:

public class Demo01Runnable {
    public static void main(String[] args) {
        // 匿名内部类
        Runnable task = new Runnable() {
            @Override
            public void run() { // 覆盖重写抽象方法
                System.out.println("多线程任务执行!");
            }
        };
        new Thread(task).start(); // 启动线程
    }
}

本着“一切皆对象”的思想,这种做法是无可厚非的:首先创建一个Runnable接口的匿名内部类对象来指定任务内容,再将其交给一个线程来启动。

代码分析

对于Runnable的匿名内部类用法,可以分析出几点内容:

  • Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;

  • 为了指定run的方法体,不得不需要Runnable接口的实现类;

  • 为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;

  • 必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;

  • 而实际上,似乎只有方法体才是关键所在


1.3 编程思想转换

做什么,而不是怎么做

我们真的希望创建一个匿名内部类对象吗?不。我们只是为了做这件事情而不得不创建一个对象。我们真正希望做的事情是:将run方法体内的代码传递给Thread类知晓。

传递一段代码——这才是我们真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。那,有没有更加简单的办法?如果我们将关注点从“怎么做”回归到“做什么”的本质上,就会发现只要能够更好地达到目的,过程与形式其实并不重要。

生活举例

当我们需要从北京到上海时,可以选择高铁、汽车、骑行或是徒步。我们的真正目的是到达上海,而如何才能到达上海的形式并不重要,所以我们一直在探索有没有比高铁更好的方式——搭乘飞机。

而现在这种飞机(甚至是飞船)已经诞生:2014年3月Oracle所发布的Java 8(JDK 1.8)中,加入了Lambda表达式的重量级新特性,为我们打开了新世界的大门。

2.1Lambda语法和使用

Lambda表达式就是对匿名内部类对象的一种格式的简化。

Java8中引入了一个新的操作符“->”,称为箭头运算符,或者Lambda运算符。

作用:就是用于分隔前后两部分的:

  • 左边:表示Lambda表达式的参数列表(接口中,定义的抽象方法的参数)。

  • 右边:表示的是方法的方法体,Lambda体。

语法格式1:没有参数,也没有返回值

() -> System.out.println(“Hello Lambda”);

语法格式2:有一个参数,没有返回值

(x) -> System.out.println(x * x);
​
说明:如果只有一个参数,那么小括号可以省略

语法格式3:有多个参数,没有返回值,格式和语法格式2相同

(x, y) -> System.out.println(x + y);

语法格式4:接口中需要重写的方法,方法内容有多句,需要给多句话加上大括号

(x, y) -> {int result = x + y; return result;}

注意事项:如果Lambda体中语句只有一句,那么大括号可以省略不写;如果大括号中只有一条语句,并且是return语句,那么return关键字也可以省略不写(如果要省略return关键字,就必须省略大括号)

例如:(x, y) -> x + y

 

示例代码

interface Inter1{
    void sayHello();
}
​
interface Inter2{
    void introduce(String name);
}
interface Inter3{
    
    void test3(int x,int y); 
}
​
interface Inter4{
    int add(int a,int b);
}


public class Demo03_Lambda表达式语法2 {
​
    public static void main(String[] args) {
        //test1_无参无返回值();
        //test2_有参无返回值();
        //test3_多参无返回值();
        test4_有参有返回值();
    }
​
    public static void test1_无参无返回值(){
        //使用匿名内部类方式
        Inter1 i1 = new Inter1(){
            public void sayHello() {
                System.out.println("hello");
            }
        };
        i1.sayHello();
​
        //使用Lambda表达式
        Inter1 i2 = ()->System.out.println("hello lambda");
        i2.sayHello();
    }
​
​
    public static void test2_有参无返回值(){
        // 匿名内部类方式
        Inter2 i1 = new Inter2(){
            public void introduce(String name) {
                System.out.println("大家好,我是: "+name);
            }
        };
        i1.introduce("渣渣辉");
​
        //使用Lambda表达式
        Inter2 i2 = (name)-> System.out.println("Lambda:大家好,我是: "+name);
        i2.introduce("古天乐");
    }
​
    public static void test3_多参无返回值(){
​
        // 匿名内部类方式
        Inter3 i1 = new Inter3(){
            public void test3(int x, int y) {
                System.out.println(x-y);
            }
        };
        i1.test3(100,90);
​
        // Lambda表达式方式
        Inter3 i2 = (a,b)->System.out.println(a-b);
        i2.test3(200, 90);
​
    }
​
    public static void test4_有参有返回值(){
​
        // 匿名内部类方式
        Inter4 i1 = new Inter4() {
            public int add(int a, int b) {
                int result = a+b;
                return result;
            }
        };
        System.out.println(i1.add(1,2));
​
​
        //Lambda表达式方式
        //Inter4 i2 = (x,y)->{int result = x+y;return result;};
        Inter4 i2 = (x,y)->x+y;
        System.out.println(i2.add(10, 20));
    }
}

二、函数式接口

1.1概述

Lambda表达式使用的前提,就是接口必须是一个函数式接口。

定义

如果在接口中,只有一个抽象方法,那么这个接口就是函数式接口。(可以包含其他方法,比如:默认方法、静态方法。。。)

defalt 。。

static 。。

格式

使用注解来检查当前接口是否是一个函数式接口

@FunctionalInterface

如果不是函数式接口,则编译报错。

作用

主要用于函数式编程(即使用lambda表达式编程)。

public class Demo01_函数式接口 {
    
    public static void main(String[] args) {
        
        Inter1 i = new Inter1(){
            public void test1(){
                System.out.println("hello");
            }
        };
    }
    
    // Inter1的对象作为参数
    public static void method1(Inter1 i){
        i.test1();
    }
    
    //Inter1的对象作为返回值
    public static Inter1 method2(){
       /*   
        Inter1 i = new Inter1(){
            public void test1(){
                System.out.println("hello");
            }
        };
        */
        Inter1 i = ()->System.out.println("hello");
        return i;
    }
}
​
@FunctionalInterface
interface Inter1{
    void test1();
}
​
//@FunctionalInterface
interface Inter2{     // 没有方法
    
}
​
//@FunctionalInterface
interface Inter3{      // 有多个方法
    void test1();
    void test2();
}


1.2常用内置函数式接口

Java8中提供了一些常用的函数式接口,在使用类似功能的时候,不需要额外定义接口,直接使用jdk中提供的即可。

函数式接口参数类型返回类型说明
Consumer<T>消费型接口Tvoid对类型为T的对象操作,方法:void accept(T t)
Supplier<T>供给型接口T返回类型为T的对象,方法:T get();可用作工厂
Function<T, R>函数型接口TR对类型为T的对象操作,并返回结果是R类型的对象。方法:R apply(T t);
Predicate<T>断言型接口Tboolean判断类型为T的对象是否满足条件,并返回boolean 值。方法boolean test(T t);

1.3消费型接口

Consumer<T>

抽象方法

void accept(T t);

作用

当某个函数可以接收一个数据,并且处理这个数据,处理完成之后,不需要返回任何数据,这个函数需要当做数据来进行传递,就使用消费型接口。

public class Demo02_消费型接口_Consumer {
​
    public static void main(String[] args) {
        /*  
        Consumer<String> con = new Consumer<String>() {
            public void accept(String str){
                System.out.println(str);
            }
        };
        test(con,800,"买书");
​
        */
        Consumer<String> con = (str)->System.out.println(str);
        test(con,800,"买书");
    }
​
    public static void test(Consumer<String> con,int money,String str){
        System.out.println("花了"+money+"块钱");
        con.accept(str);
    }
​
}


1.4方法引用

写一个函数式接口时,方法的实现(lambda体),已经被某个其他的对象实现了,就不需要在Lambda体中,再次调用这个实现,而可以直接使用那个已经定义好的方法。

格式

  • 函数式接口 名称 = 对象名 :: 方法名称

  • 函数式接口 名称 = 类名 :: 静态方法名

作用

把已经实现的方法,作为一个数据,作为一个引用,赋值给某个函数式接口的引用

可以把这个引用当做方法的返回值,也可以作为方法的实际参数进行传递。

示例代码

class Demo{
	public static void main(String[] args) {
		//1.引用JDK存在的方法
		Consumer<String> con = (x)-> System.out.println(x);
		con.accept("zhangsan");
		//下面代码引用了System.out对象的println方法
		Consumer<String> con1 = System.out::println;
		con1.accept("zhangsan");

		//2.引用其他自定义类已经存在的普通方法
		MyClass m = new MyClass();
		Consumer<Integer> con2 = m::test1;
		con2.accept(7);

		//3.引用其他类中已经存在的静态方法
		Consumer<Integer> con3 = MyClass::test3;
		con3.accept(6);
	}

}

class MyClass{
	public void test1(int x){
		x = x+1;
		x++;
		x = x-7;
		System.out.println(x);
	}

	public void test2(int x){
		x = x+1;
		x++;
		x = x-6;
		System.out.println(x);
	}

	public static void test3(int x){
		x = x*3;
		x++;
		System.out.println(x);
	}
}

1.5供给型接口

Supplier<T>

抽象方法

T get()

作用

如果需要定义函数,可以生产一个需要的数据,这个函数需要当做数据来进行传递,那么就可以使用供给型接口。

示例代码

public class Demo04_供给型接口_Supplier {
   /*
	 定义一个方法,返回一个集合,集合中n个满足要求的数据
	 
	 要求数据是随机数字,要在1-10之间
    * */
	
	public static void main(String[] args) {
		
		Supplier<Integer> sup = ()->{
			Random random = new Random();
			int num = random.nextInt(10)+1;
			return num;
		};
		
		List<Integer> list = getNum(5,sup);
		
		for(int x:list){
			System.out.println(x);
		}
		
	}
	
	public static List<Integer> getNum(int n,Supplier<Integer> sup){
		List<Integer> list = new ArrayList<Integer>();
		for(int i=1;i<=n;i++){
			list.add(sup.get());
		}
		return list;
	}
}

1.6函数型接口

Function<T, R>

抽象方法

R apply(T t)

作用

如果需要定义一个函数,接收一个数据,将数据进行处理,完成之后,还能返回一个结果,就可以使用函数型接口。

提供功能

andThen(Function f):在调用者处理方式之后,再进行参数的处理方式处理

需求:定义一个方法,传入一个String的变量, 把字符串转换成数字 并乘10后返回。

public class Demo05_函数型接口 {
   
    public static void main(String[] args) {
        /*      Function<String,Integer> fun = (str)->{
            int x = Integer.parseInt(str);
            x=x*10;
            return x;
        };
        */
        Function<String,Integer> fun = (str)-> Integer.parseInt(str)*10;
        int result = method("10",fun);
        System.out.println(result);
    }
    /*
     * str 是原数据 (要经过处理的数据)
     * 
     * fun 是处理方式
     * */
    public static int method(String str,Function<String,Integer> fun){
        return fun.apply(str);
    }
}

使用andThen 增加处理方式.

public class Demo06_函数型接口2 {
    /*
     * 定义一个方法,传入一个String的变量, 把字符串转换成数字,并乘10,返回
     * 
     * 分开写的好处:更有利于代码的复用
     * */   
    public static void main(String[] args) {
        // (str)->Integer.parseInt(str);
        Function<String,Integer> fun1  = Integer::parseInt;
        Function<Integer,Integer> fun2 = (x)->x*10;
​
        int result = method("20",fun1,fun2);
        System.out.println(result);
    }
​
    public static int method(String str,Function<String,Integer> fun1,Function<Integer,Integer>fun2){
        return fun1.andThen(fun2).apply(str);
    }
}

1.7其他接口介绍

JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在java.util.function包中被提供。

函数式接口参数类型返回类型用途
BiFunction<T,U,R>T,UR对类型为T,U参数应用操作,返回R类型的结果。包含方法为Rapply(Tt,Uu);
UnaryOperator<T>(Function子接口)TT对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为Tapply(Tt);
BinaryOperator<T>(BiFunction子接口)T,TT对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为Tapply(Tt1,Tt2);
BiConsumer<T,U>T,Uvoid对类型为T,U参数应用操作。包含方法为void accept(Tt,Uu)
ToIntFunction<T>ToLongFunction<T>ToDoubleFunction<T>Tint,long,double分别计算int、long、double、值的函数
IntFunction<R>LongFunction<R>DoubleFunction<R>int,long,doubleR参数分别为int、long、double类型的函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值