Lambda表达式与函数式接口
一、Lambda表达式
1、@FunctionalInterface注解
@FunctionalInterface //使用注解的接口有且只能有一个方法
interface Person{
void eat();
}
2、Lambda表达式使用
使用lambda表达式的接口使用@FunctionalInterface注解,或者接口中的方法只能有一个方法
public class Lambda {
public static void main(String[] args) {
//JDK1.7 使用匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}).start();
//JDK1.8 使用lambda表达式
new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}).start();
}
}
3、Lambda的方法引用
1)引用静态方法: 类名称 :: static方法名称;
2)引用某个实例对象的方法: 实例化对象 :: 普通方法;
3)引用特定类型的方法: 特定类 :: 普通方法;
4)引用构造方法: 类名称 :: new ;
当lambda参数和方法体所调用方法的参数一致(有且只调用一个方法)
class Dog {
int total = 10;
// 第一个参数为this
int eat(Dog this,int num) {
return this.total - num;
}
}
public class MethodQuote {
public static void main(String[] args) {
// 方法引用
Consumer<String> consumer = System.out::println;
consumer.accept("data");
// 静态方法的方法引用
Function<Integer, String> function = String::valueOf;
String s = function.apply(100);
System.out.println(s);
// 对象实例方法引用
// 非静态方法,会默认将对象的实例化this传入实例对象方法
String str = "abc";
Supplier<String> supplier = str::toUpperCase;
System.out.println(supplier.get());
//这是一个普通方法,如果要引用普通方法,则往往都需要实例化对象,但是如果说现在你不想给出实例化对象,只是想引用这个方法,则就可以使用特定类来进行引用处理。
// 使用类名做对象实例方法引用
Function<String,String> func = String::toUpperCase;
String s1 = func.apply(str);
System.out.println(s1);
// 使用类名做对象实例方法引用
Dog dog = new Dog();
BiFunction<Dog,Integer,Integer> biFunction = Dog::eat;
Integer res = biFunction.apply(dog, 2);
System.out.println(res);
// 构造函数的方法引用
Function<String,String> funcStr = String::new;
String abc = funcStr.apply("new");
System.out.println(abc);
}
}
// 结果
data
100
ABC
ABC
8
new
注意点
class Dog {
int total = 10;
// 第一个参数为this
int eat(Dog this,int num) {
return this.total - num;
}
}
public class MethodQuote {
public static void main(String[] args) {
// 非静态方法,会默认将对象的实例化this传入实例对象方法
Dog dog = new Dog();
int res = dog.eat(1);
System.out.println(res);
}
}
4、Lambda的类型推断
public class LambdaTest {
@FunctionalInterface
interface IIncrease{
int add(int x,int y);
}
public static void main(String[] args) {
//变量类型定义
IIncrease iIncrease = (x,y)->x+y;
//数组
IIncrease[] iIncreases = {Integer::sum}; //对象实例的引用
//强转
Object o = (IIncrease)(x,y)->x+y;
//返回值推断
IIncrease method = method();
}
public static IIncrease method(){
return (x,y)->x+y;
}
}
注意:在方法有重载时,传入lambda表达式会有歧义,需要制定类型。
public class LambdaTest {
@FunctionalInterface
interface IIncrease{
int increase(int x,int y);
}
@FunctionalInterface
interface IDecrease{
int decrease(int x,int y);
}
public static void main(String[] args) {
LambdaTest lambdaTest = new LambdaTest();
// 默认使用类型推断
int res1 = lambdaTest.test((IIncrease) (x, y) -> x + y);
int res2 = lambdaTest.test((IDecrease) (x, y) -> x - y);
System.out.println(res1);
System.out.println(res2);
}
public int test(IIncrease iIncrease){
return iIncrease.increase(1,2);
}
public int test(IDecrease iDecrease){
return iDecrease.decrease(3,4);
}
}
5、Lambda的变量引用
java在调用方法时是值传递不是引用传递。
public class FiledQuote {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(); //list的引用不能改变
Consumer<String> consumer = s-> System.out.println(s+list);
consumer.accept("aa");
}
}
如果修改list的指向对象就会编译就会报错:Variable used in lambda expression should be final or effectively final
public class FiledQuote {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list = new ArrayList<>(); //改变list的指向
Consumer<String> consumer = s-> System.out.println(s+list);//Variable used in lambda expression should be final or effectively final
consumer.accept("aa");
}
}
**原理:**在lambda外面的list变量和里面的list变量同时指向new ArrayList<>();这个对象。如果改变外面list的指向,再对list进行修改元素,对lambda里面的list没有任何影响。所有在java编译时就报错,避免了这种错误。
二、函数式接口
一些常见的函数式接口,就在java.util.function包下面
接口 | 输入参数 | 返回值类型 | 说明 |
---|---|---|---|
Predicate | T | boolean | 断言 |
Function<T, R> | T | R | 输入T输出R的函数 |
Consumer | T | / | 消费一个数据 |
Supplier | / | T | 提供一个数据 |
UnaryOperator | T | T | 输入输出类型相同 |
BiFunction<T, U, R> | (T, U) | R | 2个输入1个输出的函数 |
BinaryOperator | (T,T) | T | 2个输入1个输出的函数(输入输出相同) |
BiConsumer<T, U> | (T,U) | / | 消费两个数据 |
BiPredicate<T, U> | (T, U) | boolean | 可以传入两个参数的断言 |
1、Predicate
1)基本使用
public class PredicateTest {
public static void main(String[] args) {
//断言
Predicate<Integer> predicate = i -> i > 0; //调用test() 大于0为true
System.out.println(predicate.test(5));// 结果true
Predicate<Integer> other = i -> i > 3; //调用test() 大于3为true
// && 运算 两个Predicate都必须返回true,才为true
Predicate<Integer> and = predicate.and(other);
System.out.println(and.test(2));// 结果 false
// || 运算 两个Predicate一个返回true,结果为true
Predicate<Integer> or = predicate.or(other);
System.out.println(or.test(2));// 结果true
// 取反 !非运算
Predicate<Integer> negate = predicate.negate();
System.out.println(negate.test(-1));// 结果true
// ==运算
Predicate<Integer> equal = Predicate.isEqual(null);
System.out.println(equal.test(null)); // 结果true
// 取反
Predicate<Integer> not = Predicate.not(predicate);
System.out.println(not.test(1));// 结果true
}
}
2)使用实例
**需求:**取出在1~20间 查找出(5,15]区间的偶数
// 取出在1~20间 查找出(5,15]区间的偶数
int[] numbers= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
List<Integer> list=new ArrayList<>();
for(int i:numbers) {
list.add(i);
}
Predicate<Integer> p1=i->i>5;
Predicate<Integer> p2=i->i<=15;
Predicate<Integer> p3=i->i%2==0;
List test=list.stream().filter(p1.and(p2).and(p3)).collect(Collectors.toList());
System.out.println(test.toString());
//结果
[6, 8, 10, 12, 14]
如果我们现在需求变量需要找出(5,15]区间的奇数数,我们就可以使用negate() 和 not()方法
// 只需将修改成如下
// 方法1:
List test=list.stream().filter(p1.and(p2).and(p3.negate())).collect(Collectors.toList());
// 方法2:
List test=list.stream().filter(p1.and(p2).and(Predicate.not(p3))).collect(Collectors.toList());
//not() 方法底层也是调用p3的negate()方法
//结果
[7, 9, 11, 13, 15]
如果我们现在需求又加了一项,筛选出结果中某一个值
List test=list.stream().filter(p1.and(p2).and(Predicate.not(p3).and(Predicate.isEqual(7)))).collect(Collectors.toList());
//结果
[7]
2、Consumer
类型扩展Consumer接口有:
IntConsumer
DoubleConsumer
LongConsumer
1)基本使用
public class ConsumerTest {
public static void main(String[] args) {
// 消费一个数据
Consumer<Integer> consumer = i-> System.out.println(i);
consumer.accept(1);
System.out.println("--------------------------------");
Consumer<Integer> after = i-> System.out.println(i+i);
Consumer<Integer> andThen = consumer.andThen(after);
andThen.accept(1);
System.out.println("--------------------------------");
Consumer<Integer> after1 = i-> System.out.println(i+i+i);
Consumer<Integer> andThen1 = andThen.andThen(after1);
andThen1.accept(1);
}
}
// 调用andThen()可进行链式消费
// 结果
1
--------------------------------
1
2
--------------------------------
1
2
3
2)使用案例
public class ConsumerTest {
public static void main(String[] args) {
String[] UserInfo = {"张三,19","李四,20","王五,18"};
printUser(UserInfo,(user)->{
String name = user.split(",")[0];
System.out.print("姓名:"+name+" ");
},(user)->{
String age = user.split(",")[1];
System.out.println("年龄:"+age);
});
}
public static void printUser(String[] arr,Consumer<String> consumer,Consumer<String> consumer1){
for (String usr : arr) {
consumer.andThen(consumer1).accept(usr);
}
}
}
// 结果
姓名:张三 年龄:19
姓名:李四 年龄:20
姓名:王五 年龄:18
3、Supplier
类型扩展Supplier接口有:
BooleanSupplier
IntSupplier
DoubleSupplier
LongSupplier
1)基本使用
public class SupplierTest {
public static void main(String[] args) {
Supplier<Integer> supplier = ()->2;
System.out.println(supplier.get());
}
}
// 结果
2
2)使用案例
求数组中的最大值
public class SupplierTest {
public static void main(String[] args) {
int nums[] = {1,9,8,11,45,33,6,9};
int max = getMax(()->{
int maxTemp = nums[0];
for (int i = 0; i < nums.length; i++) {
if(nums[i]>maxTemp)
maxTemp=nums[i];
}
return maxTemp;
});
System.out.println(max);
}
public static int getMax(Supplier<Integer> supplier){
return supplier.get();
}
}
// 结果
45
4、Function<T, R>
输入值类型扩展Function接口有:
IntFunction 输入值必须为int
DoubleFunction 输入值必须为double
LongFunction 输入值必须为long
输出值类型扩展Function接口有:
ToIntFunction 输出值必须为int
ToDoubleFunction 输出值必须为double
ToLongFunction 输出值必须为long
输入出值类型扩展Function接口有:
IntToDoubleFunction 输人值必须为int,输出值必须为double
IntToDoubleFunction 输人值必须为int,输出值必须为long
DoubleToIntFunction 输人值必须为double,输出值必须为int
DoubleToLongFunction 输人值必须为double,输出值必须为long
LongToDoubleFunction 输人值必须为long,输出值必须为double
LongToIntFunction 输人值必须为long,输出值必须为int
1)基本使用
public class FunctionTest {
public static void main(String[] args) {
Function<Integer,String> fun = i->i+" hello";
System.out.println(fun.apply(7));
//fun在fun1之前执行
Function<String,String> fun1 = i->i+" word";
Function<Integer, String> compose = fun1.compose(fun);
System.out.println(compose.apply(7));
//fun2在fun1后执行
Function<String,Character> fun2 = i->i.charAt(0);
Function<String, Character> andThen= fun1.andThen(fun2);
System.out.println(andThen.apply("hello"));
//传入一个什么值就返回一个什么值
Function<Integer, Integer> id = Function.identity();
System.out.println(id.apply(3));
}
}
// 结果
7 hello
7 hello word
h
3
2)使用案例
public class FunctionTest {
static class User {
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public static void main(String[] args) {
Function<User, String> function = User::getName; //user->user.getName()的引用写法
List<User> userList = new ArrayList<>();
userList.add(new User("张三", 18));
userList.add(new User("李四", 22));
userList.add(new User("王五", 25));
List<String> userName = getUserName(userList, function);
for (String name : userName) {
System.out.println(name);
}
}
public static List<String> getUserName(List<User> userList, Function<User, String> func) {
List<String> result = new ArrayList<>();
for (User user : userList) {
String name = func.apply(user);
result.add(name);
}
return result;
}
}
// 结果
张三
李四
王五
5、UnaryOperator
Function<T, R>的子类,返回类型和传值类型一致
类型扩展UnaryOperator接口有:
IntUnaryOperator
DoubleUnaryOperator
LongUnaryOperator
1)基本使用
public class UnaryOperatorTest {
public static void main(String[] args) {
UnaryOperator<Integer> unaryOperator = i->i*3;
System.out.println(unaryOperator.apply(3));
UnaryOperator<Integer> identity = UnaryOperator.identity();
System.out.println(identity.apply(5));
}
}
// 结果
9
5
6、BiFunction<T, U, R>
1)基本使用
public class BiFunctionTest {
public static void main(String[] args) {
BiFunction<Integer,Double,String> biFunction = (i,j)->i+""+j;
System.out.println(biFunction.apply(1, 2.1));
Function<String,Double> after = Double::parseDouble;
BiFunction<Integer, Double, Double> andThen= biFunction.andThen(after);
Double res = andThen.apply(2, 2.1);
System.out.println(res);
System.out.println(res.getClass().getName());
}
}
// 结果
12.1
22.1
java.lang.Double
7、BinaryOperator
BinaryOperator继承BiFunction<T, T, T>
类型扩展BinaryOperator接口有:
IntBinaryOperator
DoubleBinaryOperator
LongBinaryOperator
1)基本使用
public class BinaryOperatorTest {
public static void main(String[] args) {
// 两个输入 一个输出 类型都相同
BinaryOperator<Integer> binaryOperator = (i,j)->i*j;
System.out.println(binaryOperator.apply(3, 4));
// 求两个数最大值
BinaryOperator<Integer> maxBinaryOperator = BinaryOperator.maxBy(Integer::compareTo); //实例对象的引用
System.out.println(maxBinaryOperator.apply(1, 2));
// 求两个数最小值
BinaryOperator<Integer> minBinaryOperator = BinaryOperator.minBy((a,b)->a.compareTo(b));
System.out.println(minBinaryOperator.apply(1, 3));
}
}
// 结果
12
2
1
8、BiConsumer<T, U>
1)基本使用
public class BiConsumerTest {
public static void main(String[] args) {
BiConsumer<String,String> biConsumer = (i,j)-> System.out.println(i+" "+j);
BiConsumer<String,String> after = (i,j)-> System.out.println(j+" "+i);
biConsumer.accept("hello","world");
System.out.println("------------------------------");
BiConsumer<String, String> biConsumer2 = biConsumer.andThen(after);
biConsumer2.accept("hello","world");
}
}
// 结果
hello world
------------------------------
hello world
world hello
9、BiPredicate<T, U>
public class BiPredicateTest {
public static void main(String[] args) {
BiPredicate<Integer, String> biPredicate = (i, j) -> i > 0 && j.length() > 1;
System.out.println(biPredicate.test(1, "a"));
System.out.println(biPredicate.test(1, "ab"));
}
}
其他方法使用与Predicate一样
三、级联表达式和柯理化
public class CurryTest {
public static void main(String[] args) {
// 级联表达式
Function<Integer,Function<Integer,Function<Integer,Integer>>> func = x->y->z->x+y+z;
// 柯理化:将多个参数的函数转成一个参数的函数链
// 目的:把函数标准化
Integer res = func.apply(1).apply(2).apply(3);
System.out.println(res);
// 函数标准化的好处
int[] nums = {1,2,3,4,5,6};
Function f= func;
for (int i = 0; i < nums.length; i++) {
if(f instanceof Function){
Object obj = f.apply(nums[i]);
if(obj instanceof Function){
f = (Function)obj;
}else {
System.out.println("结果:"+obj);
}
}
}6
}
}