Lambda表达式
-
Lambda表达式是Java SE 8中一个重要的新特性.
-
lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。Lambda表达式还增强了集合库。
-
Java Lambda表达式的一个重要用法是简化某些 匿名内部类(Anonymous Classes)的写法 。实际上Lambda表达式并 不仅仅是匿名内部类的语法糖 ,JVM内部是通过 invokedynamic指令 来实现Lambda表达式的。
Lambda并不是内部类的语法糖
Labmda表达式不是匿名内部类的语法糖,但是它也是一个语法糖。实现方式其实是依赖了几个JVM底层提供的lambda相关api。
如果是匿名内部类的语法糖,那么编译之后会有两个class文件,但是,包含lambda表达式的类编译后只有一个文件。
Lambda表达式语法 ->
-> : 该操作符被称 为 Lambda 操作符或箭头操作符
- 左侧:指定了 Lambda 表达式需要的所有参数
- 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
基本语法格式
/**
* 语法格式一:无参,无返回值,Lambda 体只需一条语句。
*/
Runnable r1=()-> System.out.println("hello");
Thread thread=new Thread(r1);
thread.start();
/**
* 语法格式二:Lambda 需要一个参数。
*/
Consumer<String> con = (x)-> System.out.println(x);
/**
* 语法格式三:Lambda 只需要一个参数时,参数的小括号可以省略。
*/
Consumer<String> con2 = x -> System.out.println(x);
/**
* 语法格式四:Lambda 需要两个参数,并且有返回值。
*/
Comparator<Integer> com = (x, y) -> {
System.out.println("函数式接口");
return Integer.compare(x, y);
};
/**
* 语法格式五:当 Lambda 体只有一条语句时,return 与大括号可以省略。
*/
Comparator<Integer> com2 = (x, y) -> Integer.compare(x, y);
Comparator<Integer> com = (Integer x,Integer y) -> { //Integer 类型可以省略
System.out.println("函数式接口");
return Integer.compare(x, y);
};
Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即 “类型推断”
-----》这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda表达式的类型依赖于上下文环境,是由编译器推断出来的。
使用Lamdba表达式的要求:
- 必须有相应的 函数接口 :内部只有一个抽象方法的接口,这种类型的接口也称为 SAM接口 ,即Single Abstract Method interfaces
自定义函数接口
只需要编写一个只有一个抽象方法的接口即可
// 自定义函数接口
@FunctionalInterface
public interface ConsumerInterface<T>{
void accept(T t);
}
@FunctionalInterface 可加可不加:加上该标注编译器会帮你检查接口是否符合函数接口规范
ConsumerInterface<String> consumer = str -> System.out.println(str);
consumer.accept("我是自定义函数式接口");
完整:
@FunctionalInterface
public interface ConsumerInterface<T> {
void show(T t);
class TestStream<T>{
private List<T> list;
public void myForEach(ConsumerInterface<T> consumer){
for(T t:list){
consumer.show(t);
}
}
public void setList(List<T> list){
this.list=list;
}
}
}
public class LamdbaTest2 {
public static void main(String[] args) {
ConsumerInterface.TestStream<String> stream=new ConsumerInterface.TestStream<>();
List list= Arrays.asList("11","22","33");
stream.setList(list);
// 使用自定义函数接口书写Lambda表达式
stream.myForEach(s -> System.out.println(s));
}
}
Java 内置四大核心函数式接口
Consumer 消费型接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) |
public class LambdaTest3 {
public static void main(String[] args) {
LambdaTest3 lambdaTest3=new LambdaTest3();
//Consumer<T> 消费型接口
lambdaTest3.hello("zc",m-> System.out.println("你好:"+m));
}
public void hello(String st, Consumer<String> con){
con.accept(st);
}
}
-----》你好:zc
Supplier 供给型接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Supplier<T> 供给型接口 | 无 | T | 返回类型为T的对象,包含方法: T get() |
public class LambdaTest3 {
public static void main(String[] args) {
//Function<T, R> 函数型接口:
LambdaTest3 lambdaTest3=new LambdaTest3();
List<Integer> numList=lambdaTest3.getNumList(2,()->(int)(Math.random()*100));
for (Integer num : numList) {
System.out.println(num);
}
}
public List<Integer> getNumList(int num, Supplier<Integer> sup){
List<Integer> list=new ArrayList<>();
for(int i=0;i<num;i++){
Integer n=sup.get();
list.add(n);
}
return list;
}
}
-----》62 48
Function<T, R> 函数型接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Function<T, R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类的对象。包含方法:R apply(T t); |
public class LambdaTest3 {
public static void main(String[] args) {
LambdaTest3 lambdaTest3=new LambdaTest3();
String newStr =lambdaTest3.stringHandler("\t\t\t 这是一个函数型接口 ", (str) -> str.trim());
//String.trim() 去掉字符串首尾的空格
System.out.println(newStr);
String subStr = lambdaTest3.stringHandler("这是一个函数型接口", (str) -> str.substring(4, 7));
System.out.println(subStr);
}
public String stringHandler(String str, Function<String,String> fun){
return fun.apply(str);
}
}
----》这是一个函数型接口 函数型
Predicate 断定型接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Predicate<T> 断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法Boolean test(T t); |
public class LambdaTest3 {
public static void main(String[] args) {
LambdaTest3 lambdaTest3=new LambdaTest3();
List<String> list = Arrays.asList("Hello", "Java8", "Lambda", "www", "ok");
List<String> stringList=lambdaTest3.filterStr(list,(s)->s.length()>3);
stringList.forEach(System.out::println);
}
public List<String> filterStr (List<String> list, Predicate<String> pre){
List<String> stringList=new ArrayList<>();
for(String str:list){
if(pre.test(str)){
stringList.add(str);
}
}
return stringList;
}
}
----》Hello Java8 Lambda
其他接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
BiFunction<T,U,R> | T,U | R | 对类型T,U参数应用操作,返回R类型的结果。包含方法R apply(T t,U u) |
UnaryOperator<T> | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法 T apply(T t); |
BinaryOperator<T>(BiFunction子接口) | T,T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法 T apply(T t1,T t2); |
BiConsumer<T,U> | T,U | void | 对类型为T,U参数应用操作。包含方法为 void accept(T t,U u) |
TolntFunction<T> ToLongFunction<T> ToDoubleFunction<T> | T | int long double | 分别计算 int \long\double值得函数 |
IntFunciton<R> LongFunction<R> DoubleFunction<R> | int\long\double | R | 参数分别为int\long\double类型得函数 |
方法引用和构造器引用
方法引用
使用操作符 “::” 将方法名和对象或类的名字分隔开来
前提条件:
- 方法引用所引用的方法的 参数列表 必须要和函数式接口中抽象方法的参数列表相同(完全一致)。
- 方法引用所引用的方法的的 返回值 必须要和函数式接口中抽象方法的返回值相同(完全一致)。
方法引用的格式:
- 实例对象名::实例方法名
- 类名::静态方法名
- 类名::实例方法名
2和3的区别:若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式:类名::实例方法名。
1、对象::实例方法
public void test1(){
PrintStream ps =System.out;
Consumer<String> con=(str)->ps.println(str);
con.accept("Hello World!");
System.out.println("--------------------------------");
Consumer<String> con2=ps::println;
con2.accept("con2");
System.out.println("--------------------------------");
Consumer<String> con3=System.out::println;
con3.accept("con3");
}
public static void main(String[] args) {
LambdaTest4 lambdaTest4=new LambdaTest4();
lambdaTest4.test1();
}
—》
Hello World!
con2
con3
public class LambdaTest4 {
public void test2(){
Employee emp=new Employee(1,"cc",18,5112.99);
Supplier<String> sup=()->emp.getName();
System.out.println(sup.get());
System.out.println("----------------------------------");
Employee emp1 = new Employee(2, "bb", 18, 5112.99);
Supplier<String> sup2 = emp1::getName;
System.out.println(sup2.get());
}
public static void main(String[] args) {
LambdaTest4 lambdaTest4=new LambdaTest4();
lambdaTest4.test2();
}
}
----》
cc
bb
2、类::静态方法
public class LambdaTest4 {
public void test3(){
BiFunction<Double,Double,Double> fun=(x,y)->Math.max(x,y);
System.out.println(fun.apply(1.5,22.2));
System.out.println("-------------------------test3-------------------------");
BiFunction<Double,Double,Double> fun2=Math::max;
System.out.println(fun2.apply(1.2,1.5));
}
public static void main(String[] args) {
LambdaTest4 lambdaTest4=new LambdaTest4();
lambdaTest4.test3();
}
}
----》
22.2
-------------------------test3-------------------------
1.5
public void test4(){
/**
* Integer.compare()
* 对象大于目标参数,返回1
* 对象小于目标参数,返回-1
* 对象等于目标参数,返回0
*/
Comparator<Integer> com=(x,y)->Integer.compare(x,y);
System.out.println(com.compare(3,9));
System.out.println("-------------------------test4-------------------------");
Comparator<Integer> com2=Integer::compare;
System.out.println(com2.compare(3,9));
}
public static void main(String[] args) {
LambdaTest4 lambdaTest4=new LambdaTest4();
lambdaTest4.test4();
}
—》 -1 -1
3、类::实例方法
public void test5(){
BiPredicate<String,String> bp=(x,y)-> x.equals(y);
System.out.println(bp.test("abcde", "abcde"));
System.out.println("-----------------------------------------");
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));
public static void main(String[] args) {
LambdaTest4 lambdaTest4=new LambdaTest4();
lambdaTest4.test5();
}
—》 true true
构造器引用
前提条件:
- 构造器 参数列表 要与接口中抽象方法的参数列表一致
构造器引用的格式:
- 类名 :: new
// Employee类中必须有一个 Employee(String name, int age) 的构造器
BiConsumer<String,Integer> biConsumer=Employee::new;
biConsumer.accept("ll",19);
数组引用
public class LambdaTest5 {
public void test(){
//传统Lambda实现
Function<Integer,int[]> function=(i)->new int[i];
int[] apply=function.apply(10);
System.out.println(apply.length);//10
//数组类型引用实现
function=int[]::new;
apply=function.apply(100);
System.out.println(apply.length);//100
}
public static void main(String[] args) {
LambdaTest5 lambdaTest5=new LambdaTest5();
lambdaTest5.test();
}
}
Collections中的常用函数接口
Java8新增了java.util.funcion包,里面包含常用的函数接口,Java集合框架也新增部分接口,以便与Lambda表达式对接
Java集合框架的接口继承结构:
Collection中的新方法
forEach()
- 该方法的签名为void forEach(Consumer action)
- 作用是对容器中的每个元素执行action指定的动作
- 其中Consumer是个函数接口,里面只有一个待实现方法void accept(T t)
public static void main(String[] args) {
//匿名内部类实现:
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(3, 6, 9, 10));
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
if (integer % 3 == 0) {
System.out.println(integer);//3,6,9
}
}
});
//Lambda实现:
list.forEach((s) -> {
if (s % 3 == 0) {
System.out.println(s);//3,6,9
}
});
}
removeIf()
- 该方法签名为boolean removeIf(Predicate filter)
- 作用是删除容器中所有满足filter指定条件的元素
- 其中Predicate是一个函数接口,里面只有一个待实现方法boolean test(T t)
//匿名内部类实现:
ArrayList<Integer> list2 = new ArrayList<>(Arrays.asList(3, 6, 9, 10));
list2.removeIf(new Predicate<Integer>() {
@Override
public boolean test(Integer sum) {
return sum % 3 == 0;
}
});
System.out.println(list2);//[10]
//lambda实现:
list2=new ArrayList<>(Arrays.asList(3, 6, 9, 10));
list2.removeIf(s -> s % 3 == 0);
System.out.println(list2);//[10]
replaceAll()
从入门到入土:Lambda完整学习指南,包教包会!:https://mp.weixin.qq.com/s/ogcRRWBE8CtOY5l6kcQvhg