Java8新特性之函数式接口及Lambda表达式

概述

所谓函数式接口,即接口中有且只有一个抽象方法的接口。在jdk8中用注解“@FunctionalInterface"来声明一个接口是函数式接口。当自己新建接口时,可以不用加这个注解,只要新建的接口满足函数式接口的条件,就会被编译器隐式为函数式接口,为了规范一般建议还是带上注解。有了这个注解,当接口中没有抽象方法或有两个以上的抽象方法时,编译器会报错,可以避免人为失误。

接口方法形式

根据函数式接口的抽象方法的返回值和参数来分,基本可分为:无返回值无参数、无返回值单参数、无返回值多参数、有返回值无参数、有返回值单参数、有返回值多参数等六种形式。根据函数式接口功能来分,基本可分为:工厂型、判断型、消费型、供给型,其实后几种类型都可用工厂型模拟出来。

无返回值无参数

代表Runnable接口。一般主要用于处理业务逻辑,没有输入和输出。

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

无返回值单参数

代表Consumer<T>接口。本质是一个消费者,来什么,消费什么,有输入,没输出。

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

无返回值多参数

代表BiConsumer<T, U>接口。本质一个更能吃的消费者,多输入,没输出。

@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T t, U u);
}

有返回值无参数

代表Supplier<T>接口。本质是一个生产者,没输入,有输出。

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

有返回值单参数

代表Function<T, R>。本质是个加工厂,有输入,有输出。Predicate<T>接口本质产出某种特定物品的加工厂。

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

有返回值多参数

代表BiFunction<T, U, R>接口。本质大型加工厂,多输入,有输出。

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}

Lambda表达式

Lambda表达式是基于函数式接口来的,相对于之前的匿名内部类这种复杂的写法来说,Lambda表达式是一种极其精简的写法。没有函数式接口,就没法写Lambda表达式。如果把函数式接口比作数学中的一个命题,那么Lambda表达式就是这个命题的精简版的证明过程(匿名内部类写法是一般证明过程)。Lambda表达式是对函数式接口的一种高度概括,一个Lambda表达式整体就代表一个函数式接口的实例对象。抛开函数式接口的Lambda表达式只是一些字符而已,没有任何意义。

举例说明
Function<Integer, Integer> f1 = n->n+1; 这里的n->n+1是Function接口的子类实现类的一个匿名对象实例,这个实例方法的行为是把一个给定数字加1。
如果只有n->n+1,没有前面的函数式接口的申明,那这里的n->n+1将毫无意义可言,就是一堆字符而已。

/** 
 n->n+1这个整体是Function接口的子类实现类的一个匿名对象,和下面的写法是等价的
 new Function<Integer, Integer>() {
		@Override
		public Integer apply(Integer t) {
			return t+1;
		}
	};
*/
Function<Integer, Integer> f1 = n->n+1;
Function<Integer, Integer> f = new Function<Integer, Integer>() {
		@Override
		public Integer apply(Integer t) {
			return t+1;
		}
	};

形式

根据输入参数多少,输出语句多少,有不同写法,原则就是:单一参数(或无参数的)的小括号可省略;单一语句的(有返回值的单一语句,大括号和return要同时省略)大括号可省略。通用写法如:(a,b,c…)->{…},小括号里是输入参数,大括号里是方法体内容(对于有返回值方法来说,最后要加上return)。下面是具体写法:

情况单语句写法多语句写法
通用情况(n1,n2,n3…) -> {…}(n1,n2,n3…) -> {…}
无返回值无参数() -> …() -> {…}
无返回值单参数n -> …n -> {…}
无返回值多参数(n1,n2) -> …(n1,n2) -> {…}
有返回值无参数() -> r() -> {…;return r;}
有返回值单参数n -> rn -> {…;return r;}
有返回值多参数(n1,n2) -> r(n1,n2) -> {…;return r;}

具体例子如下代码:

package com.test;

import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class TestLambda {
	public static void main(String[] args) {
		
		//通用写法
		Runnable r = ()->{System.out.println("开始线程...");};
		//无参,单语句无返回
		Runnable r1 = ()->System.out.println("开始线程...");
		//无参,多语句无返回
		Runnable r2 = ()->{System.out.println("开始线程...");System.out.println("线程执行中...");};
		
		//通用写法
		Consumer<Integer> c = n -> {System.out.println(n);};
		//单参数,单语句无返回
		Consumer<Integer> c1 = n -> System.out.println(n);
		//单参数,多语句无返回
		Consumer<Integer> c2 = n -> {System.out.println("开始打印中...");System.out.println(n);};
		
		//通用写法
		BiConsumer<Integer, Integer> bic = (n1,n2)->{System.out.println(n1+n2);};
		//多参数,单语句无返回
		BiConsumer<Integer, Integer> bic1 = (n1,n2)->System.out.println(n1+n2);
		//多参数,多语句无返回
		BiConsumer<Integer, Integer> bic2 = (n1,n2)->{int n =n1+n2;System.out.println(n);};
		
		//通用写法
		Supplier<Integer> s = ()->{return 1;};
		//无参,单语句返回
		Supplier<Integer> s1 = ()->1;
		//无参,多语句返回
		Supplier<Integer> s2 = ()->{System.out.println("开始获取...");return 1;};
		
		//通用写法
		Function<Integer, Integer> f = (n)->{return n+1;};
		//单参数,单语句返回
		Function<Integer, Integer> f1 = n->n+1;
		//单参数,多语句返回
		Function<Integer, Integer> f2 = n->{System.out.println("输入了"+n);return n+1;};
		
		//通用写法
		BiFunction<Integer, Integer, Integer> bif = (n1,n2)->{return n1+n2;};
		//多参数,单语句返回
		BiFunction<Integer, Integer, Integer> bif1 = (n1,n2)->n1+n2;
		//多参数,多语句返回
		BiFunction<Integer, Integer, Integer> bif2 = (n1,n2)->{System.out.println("输入的n1,n2参数分别为:"+n1+","+n2);return n1+n2;};
	}
}

用法

按照函数式接口里定义的方法的入参和返回值的实际情况,选择一种合适的表达式形式

package com.test;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

public class TestUseLambda {
	public static void main(String[] args) {
		Function<Integer, Integer> f1 = n -> n+1;
		Function<Integer, Integer> f2 = n -> 2*n;
		Function<Integer, Integer> f3 = n -> 2*n+1;
		Function<Integer, Integer> f4 = n -> (n+1)*2;
		List<Integer> list = new ArrayList<>();
		list.add(1);
		list.add(2);
		list.add(3);
		System.out.println("=================n -> 2*n+1=====================");
		//对list集合的每个元素先乘以2然后在加1 ,即 n -> 2*n+1
		//func1和func11是等价的
		func1(list,f1,f2);//可以先定义好一个父类引用
		func1(list,n -> n+1,n -> 2*n);//也可现用现写
		func11(list,f1,f2);
		func11(list,n -> n+1,n -> 2*n);
		//两步操作合一,重新定义Function<Integer, Integer>接口的实现
		//此时的func3、func1和func11是等价的
		func3(list, f3);
		func3(list, n -> 2*n+1);
		//利用stream的流式处理,一行搞定
		list.stream().map(n->2*n+1).forEach(n->System.out.println(n));
		System.out.println("=================n ->(n+1)*2=====================");
		//对list集合的每个元素先加1然后在乘以2,即n ->(n+1)*2
		//func2和func22是等价的
		func2(list,f1,f2);
		func2(list,n -> n+1,n -> 2*n);
		func22(list,f1,f2);
		func22(list,n -> n+1,n -> 2*n);
		//两步操作合一,重新定义Function<Integer, Integer>接口的实现
		//此时的func3、func2和func22是等价的
		func3(list, f4);
		func3(list, n ->(n+1)*2);
		//利用stream的流式处理,一行搞定
		list.stream().map(n->(n+1)*2).forEach(n->System.out.println(n));
	}
	
	public static void func1(List<Integer> list, Function<Integer, Integer> f1,Function<Integer, Integer> f2) {
		for (Integer i : list) {
			System.out.print(f1.apply(f2.apply(i))+" ");
		}
		System.out.println();
	}
	public static void func11(List<Integer> list, Function<Integer, Integer> f1,Function<Integer, Integer> f2) {
		for (Integer i : list) {
			System.out.print(f1.compose(f2).apply(i)+" ");//compose可以理解为先执行f2的操作,后执行f1的操作,compose表示意思的是把一个函数的输出作为另一个函数的输入
		}
		System.out.println();
	}
	
	public static void func2(List<Integer> list, Function<Integer, Integer> f1,Function<Integer, Integer> f2) {
		for (Integer i : list) {
			System.out.print(f2.apply(f1.apply(i))+" ");
		}
		System.out.println();
	}
	public static void func22(List<Integer> list, Function<Integer, Integer> f1,Function<Integer, Integer> f2) {
		for (Integer i : list) {
			System.out.print(f1.andThen(f2).apply(i)+" ");//andThen可以理解为先执行f1,后执行f2,即f1结束后接着f2
		}
		System.out.println();
	}
	
	public static void func3(List<Integer> list, Function<Integer, Integer> f) {
		for (Integer i : list) {
			System.out.print(f.apply(i)+" ");
		}
		System.out.println();
	}
}

总结

Lambda表达式得名于数学中的 λ 演算。写Lambda表达式,就像练太极,只重其义,不重其招,不要拘泥于形式,要抓住本质。本质就是对方法更深一步的抽象,关注方法的行为(如何输入和输出),忽略方法的形式(方法在哪个类中实现,方法名称,参数列表等)。写代码时,只要明确要输入的是什么,要输出的是什么,就可以写好Lambda表达式了。实在不行,不是还有匿名内部类的写法吗,反正能用表达式的地方都能用匿名内部类。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值