Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
lamdba表达式的写法:
(parameters) -> expression
或
(parameters) ->{ statements; }
当且只有一个抽象方法的接口,称之为函数式接口,当然接口中可以包含其他的方法(默认,静态,私有)
@FunctionalInterface
这个注解的作用是可以检查接口是否是一个函数式接口 是:编译成功 否:编译失败(接口中没有抽象方法或者是抽象方法有多个)
/**
* Created by asus on 2020/6/8.
*/
/*
函数式接口:当且只有一个抽象方法的接口,称之为函数式接口
当然接口中可以包含其他的方法(默认,静态,私有)
*/
/*
@FunctionalInterface这个的作用是可以检查接口是否是一个函数式接口
是:编译成功
否:编译失败(接口中没有抽象方法或者是抽象方法有多个)
*/
//加了这个注解之后,如果是有多个抽象方法或者是没有抽象方法都会编译不通过
@FunctionalInterface
public interface MyFunctionInterface {
//这里的public abstract是可以省略的,可以写成void method()
public abstract void method();
}
实现类
/**
* Created by asus on 2020/6/8.
*/
public class MyFunctionInterfaceImpl implements MyFunctionInterface {
@Override
public void method() {
System.out.println("这是个函数式接口的实现类");
}
}
main方法中对于前两个方法的调用属于普通的调用,后面两个是lamdba表达式的使用,各个符号的含义,还有lamdba表达式的简化
/**
* Created by asus on 2020/6/8.
*/
/*
函数式接口的使用:一般可以作为方法的参数和返回值类型
*/
public class Demo {
//定义一个方法,方法的参数是函数式接口MyFunctionInterface
public static void show(MyFunctionInterface myInter)
{
myInter.method();
}
public static void main(String[] args) {
/*
第一个和第二个方法使用是正常接口的使用方法
*/
//调用show方法,方法参数一个接口,所以可以传递接口的实现类
show(new MyFunctionInterfaceImpl());
//调用show方法,方法的参数是一个接口,所以可以使用传递接接口的匿名内部类
show(new MyFunctionInterface() {
@Override
public void method() {
System.out.println("匿名内部类的使用");
}
});
//调用show方法,方法的参数是一个函数式接口,所以可以使用lambda表达式
//这里的小括号()里面写的是抽象方法的参数,大括号是方法的方法体
show(()->{
System.out.println("这是个lambda表达式");
});
//简化lambda表达式
//小括号已经是最简的了,大括号里面有且只有一行语句,就可以将大括号省略,并且输出语句的分号;也可以省略
show(()-> System.out.println("lambda表达式的简化"));
}
}
例子:日志的性能优化
1.普通的日志
/**
* Created by asus on 2020/6/8.
*/
//这里我们定义一个日志,只有等级为1才会输出后面的message
public class LogTest {
public static void log(int level,String message)
{
if(level==1)
{
System.out.println(message);
}
}
public static void main(String[] args) {
//定义三个日志信息
String mess1 = "hello";
String mess2 = " word";
String mess3 = " !";
//这里我们看到如果是级别为1,就不会造成浪费
log(1,mess1+mess2+mess3);
//但是如果级别为其他的话,mess1+mess2+mess3就会造成浪费
log(2,mess1+mess2+mess3);
}
}
这里的例子如果日志的级别不为1的话,字符串的拼接也是会执行,这样会浪费性能
2.使用lamdba表达式
例子1:
定义函数式接口
/**
* Created by asus on 2020/6/8.
*/
public interface LogLa {
public abstract String message();
}
/**
* Created by asus on 2020/6/8.
*/
/*
这里我们使用lamdba表达式,lamdba有延迟执行的特性,所以如果日志级别不是为1的话,也不会执行message()的方法
这样的话就不会造成性能的浪费
*/
public class LambdaLog {
public static void log(int level,LogLa log)
{
if(level==1)
{
System.out.println(log.message());
}
}
public static void main(String[] args) {
//定义三个日志信息
String mess1 = "hello";
String mess2 = " word";
String mess3 = " !";
//这里我们看到如果是级别为1,就不会造成浪费
log(1,()->{
return mess1+mess2+mess3;
});
//因为lamdba有延迟执行,所以并不会执行message()的方法
log(2,()-> {
return mess1 + mess2 + mess3;
});
}
}
例子2:
多线程的Runnable接口也是一个函数式接口
/**
* Created by asus on 2020/6/8.
*/
//Runnable也是一个函数式接口
public class LamdbaThread {
public static void main(String[] args) {
//普通的线程执行
MyRunnable run = new MyRunnable();
Thread thread = new Thread(run);
thread.start();
//使用Lamdba表达式
Thread thread1 = new Thread(()->{
System.out.println(Thread.currentThread().getName()+"执行了");
});
//简化Lamdba
Thread thread2 = new Thread(()->
System.out.println(Thread.currentThread().getName()+"执行了")
);
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable
{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行了");
}
}
有返回值和参数的函数式接口的使用
Comparator是一个函数式接口,我们以这个为例
import java.util.Arrays;
import java.util.Comparator;
/**
* Created by asus on 2020/6/9.
*/
//comparator是一个函数式接口
public class ComparatorLambda {
public static Comparator getComparator()
{
/*
//这里是使用的匿名内部类
return new Comparator() {
@Override
public int compare(String o1, String o2) {
//这里表示按照字符串的长度降序排序,也就是字符串长度长的在前面
return o2.length()-o1.length();
}
};
*/
/*
//使用lamdba表达式
return (o1,o2)->{
return o2.length()-o1.length();
};
*/
//简化lambda表达式
return (o1,o2)->o2.length()-o1.length();
}
public static void main(String[] args) {
String [] strings={"aaaaaaa","bbbb","cccccccccccc","d"};
System.out.println(Arrays.toString(strings));
//输出结果为[aaaaaaa, bbbb, cccccccccccc, d]
//使用arrays的sorts方法进行排序
Arrays.sort(strings,getComparator());
System.out.println(Arrays.toString(strings));
//输出的结果为[cccccccccccc, aaaaaaa, bbbb, d]
}
}
jdk8之后给我们提供了部分函数式接口,都在java.util.function包下
import java.util.function.Supplier;
/**
* Created by asus on 2020/6/9.
*/
/*
常用的函数式接口,java.util.function包下
Supplier被称之为生产型接口,指定接口的泛型是什么,那么接口中的get()方法返回的就是什么
*/
public class SupplierLamdba {
public static String getString(Supplier sup){
return sup.get();
}
public static void main(String[] args) {
//使用lamdba表达式
String message = getString(()->{
return "sipplier是个常用的函数式接口";
});
System.out.println(message);
//简化版
String message1 = getString(()->"sipplier是个常用的函数式接口2");
System.out.println(message1);
}
}
import java.util.function.Supplier;
/**
* Created by asus on 2020/6/9.
*/
//获取数组元素中的最大值
public class MaxSupplier {
public static int getMax(Supplier supplier){
return supplier.get();
}
public static void main(String[] args) {
int [] sort = {111,22,4455,75,3,6};
int maxsort=getMax(()->{
int max = sort[0];
for(int i:sort){
if(i>max)
{
max = i;
}
}
return max;
});
System.out.println(maxsort);
}
}
是一个消费型接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据
接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据
import java.util.function.Consumer;
/**
* Created by asus on 2020/6/9.
*/
public class ConsumerLamdba {
public static void getConsumer(String name, Consumerconsumer)
{
consumer.accept(name);
}
public static void main(String[] args) {
getConsumer("赵丽颖",(String name)->{
System.out.println(name);
});
//对字符串进行反转
getConsumer("赵四是个人渣",(String name)->{
StringBuilder builder = new StringBuilder(name);
String outname =builder.reverse().toString();
System.out.println(outname);
});
}
}
Consumer中的andThen方法,可以将两个Consumer接口结合起来消费
具体使用看下面的例子
import java.util.function.Consumer;
/**
* Created by asus on 2020/6/9.
*/
/*
andThen是将两个Consumer结合起来消费
*/
public class ConsumerAndThen {
public static void getAndThen(String name, Consumer con1,Consumer con2)
{
/*
//这里是两个Consumer接口分别对name进行消费
con1.accept(name);
con2.accept(name);
*/
//这里的意思跟上面是一样的,那个Consumer在前面,那个先消费
con1.andThen(con2).accept(name);
}
public static void main(String[] args) {
getAndThen("Hello",
(t)->{
//将字符串转化为大写
System.out.println(t.toUpperCase());
},(t)->{
//将字符串转化为小写
System.out.println(t.toLowerCase());
});
}
}
Demo
import java.util.function.Consumer;
/**
* Created by asus on 2020/6/9.
*/
/*
要求:将数组格式为["迪丽热巴,女","胡歌,男","马尔扎哈,兽"],打印出来的格式是 姓名:迪丽热巴 性别:女 这种形式
这里要使用两个Consumer,第一个负责消费姓名,第二个负责消费性别,最后将两个组合起来
*/
public class ConsumerTest {
public static void getMessage(String[] array, Consumer con1,Consumer con2)
{
for(String message:array)
{
con1.andThen(con2).accept(message);
}
}
public static void main(String[] args) {
String [] arr = {"迪丽热巴,女","胡歌,男","马尔扎哈,兽"};
getMessage(arr,
(message)->{
String name = message.split(",")[0];
System.out.print("姓名:"+name+" ");
},
(message)->{
String name = message.split(",")[1];
System.out.print("性别:"+name);
System.out.println();
});
}
}
import java.util.function.Predicate;
/**
* Created by asus on 2020/6/9.
*/
/*
java.util.function.Predicate接口
Predicate接口包含一个抽象方法:
boolean test(T t):用来对于制定数据类型进行判断方法
结果:
符合条件:true
不符合条件:false
*/
//判断字符串的长度是否大于5
public class PredicateLamdba {
public static boolean getPredicate(String message, Predicatepre)
{
return pre.test(message);
}
public static void main(String[] args) {
String message = "abcderfg";
Boolean b = getPredicate(message,(String m)->{
return m.length()>5;
});
System.out.println(b);
//简化版
Boolean c = getPredicate(message,(m)->m.length()>5);
System.out.println(c);
}
}
(1)Predicate接口的and方法,相当于&&
import java.util.function.Predicate;
/**
* Created by asus on 2020/6/9.
*/
/*
逻辑表达式:可以连接多个判断的条件
&&:与运算,有false则为false
||:或运算,有true则为true
!:非(取反)运算,非真则假,非假则真
需求:判断一个字符串,有两个判断的条件
1.判断字符串的长度是否大于5
2.判断字符串是否包含a
两个条件必须同时满足,我们就可以使用&&运算符连接两个条件
predicate接口中有一个方法and,表示并且关系也可以用来连接两个判断条件
*/
public class PredicateAnd {
public static boolean getAnd(String name, Predicatep1,Predicatep2)
{
//return p1.test(name)&&p2.test(name);
//这里也可以使用predicate接口的and方法
return p1.and(p2).test(name);
}
public static void main(String[] args) {
String name = "abcder";
boolean b = getAnd(name,
(String n)->{
return n.length()>5;
},
(String n)->{
return n.contains("a");
});
System.out.println(b);
}
}
(2)Predicate接口的or方法,相当于||
import java.util.function.Predicate;
/**
* Created by asus on 2020/6/9.
*/
/*
逻辑表达式:可以连接多个判断的条件
&&:与运算,有false则为false
||:或运算,有true则为true
!:非(取反)运算,非真则假,非假则真
需求:判断一个字符串,有两个判断的条件
1.判断字符串的长度是否大于5
2.判断字符串是否包含a
两个条件只需要满足其中一个,我们就可以使用||运算符连接两个条件
predicate接口中有一个方法or,表示并且关系也可以用来连接两个判断条件
*/
public class PredicateOr {
public static boolean getAnd(String name, Predicatep1,Predicatep2)
{
//return p1.test(name)||p2.test(name);
//这里也可以使用predicate接口的and方法
return p1.or(p2).test(name);
}
public static void main(String[] args) {
String name = "abcder";
boolean b = getAnd(name,
(String n)->{
return n.length()>5;
},
(String n)->{
return n.contains("a");
});
System.out.println(b);
}
}
(3)Predicate接口的negate方法,相当于!
import java.util.function.Predicate;
/**
* Created by asus on 2020/6/9.
*/
/*
逻辑运算符的取反是使用!,predicate接口的取反是使用negate方法
需求:字符串长度大于5,则为false
字符串长度不大于5,则为true
*/
public class PredicateNegate {
public static boolean getNegate(String name, Predicate p1){
//return !p1.test(name);
return p1.negate().test(name);
}
public static void main(String[] args) {
String name = "abcdefg";
boolean b =getNegate(name,(String n)->{
return n.length()>5;
});
System.out.println(b);
//输出结果为false
}
}
import java.util.function.Function;
/**
* Created by asus on 2020/6/9.
*/
/*
java.util.function.Function接口是用来根据一个类型的数据得到另一个类型的数据
前者称之为前置条件,后者称之为后置条件
Function接口中最主要的抽象方法为 R apply(T t),根据类型T的参数获取类型R的结果
使用场景例如:将String类型转换为Integer类型
*/
public class FunctionApply {
public static void getApply(String number, Functionfun)
{
Integer num = fun.apply(number);
System.out.println(num);
}
public static void main(String[] args) {
String number ="122345";
getApply(number,(String n)->{
return Integer.parseInt(n);
});
//这里是简化版本
getApply(number,(n)->Integer.parseInt(n));
}
}
import java.util.function.Function;
/**
* Created by asus on 2020/6/9.
*/
/*
需求,将字符串的数字转换为int类型之后加20
在吧int类型的数字转换为字符串
*/
public class FunctionTest {
public static void funTest(String number,Function fun1,Functionfun2)
{
// Integer num = fun1.apply(number);
// String n = fun2.apply(num);
String n = fun1.andThen(fun2).apply(number);
System.out.println(n);
}
public static void main(String[] args) {
String number = "121234";
funTest(number,(String n)->{
return Integer.parseInt(n)+20;
},(Integer nu)->{
return String.valueOf(nu);
});
}
}