欢迎光临北大青鸟佛山华大校区

 
  JavaLambda表达式是Java8引入的一个新的功能,可以说是模拟函数式编程的一个语法糖,类似于Javascript中的闭包,但又有些不同,主要目的是提供一个函数化的语法来简化我们的编码。

  Lambda基本语法

  Lambda的基本结构为(arguments)->body,有如下几种情况:

  参数类型可推导时,不需要指定类型,如(a)->System.out.println(a)

  当只有一个参数且类型可推导时,不强制写(),如a->System.out.println(a)

  参数指定类型时,有括号,如(inta)->System.out.println(a)

  参数可以为空,如()->System.out.println(“hello”)

  body需要用{}包含语句,当只有一条语句时{}可省略

  常见的写法如下:

  (a)->a*a

  (inta,intb)->a+b

  (a,b)->{returna-b;}

  ()->System.out.println(Thread.currentThread().getId())

  函数式接口FunctionalInterface

  概念

  JavaLambda表达式以函数式接口为基础。什么是函数式接口(FunctionalInterface)?简单说来就是只有一个方法(函数)的接口,这类接口的目的是为了一个单一的操作,也就相当于一个单一的函数了。常见的接口如:Runnable,Comparator都是函数式接口,并且都标注了注解@FunctionalInterface。

  举例

  以Thread为例说明很容易理解。Runnable接口是我们线程编程时常用的一个接口,就包含一个方法voidrun(),这个方法就是线程的运行逻辑。按照以前的语法,我们新建线程一般要用到Runnable的匿名类,如下:

  newThread(newRunnable(){

  @Override

  publicvoidrun(){

  System.out.println(Thread.currentThread().getId());

  }

  }).start();

  如果写多了,是不是很无聊,而基于Lambda的写法则变得简洁明了,如下:

  newThread(()->System.out.println(Thread.currentThread().getId())).start();

  注意Thread的参数,Runnable的匿名实现就通过一句就实现了出来,写成下面的更好理解

  Runnabler=()->System.out.println(Thread.currentThread().getId());

  newThread(r).start();

  当然Lambda的目的不仅仅是写起来简洁,更高层次的目的等体会到了再总结。

  再看一个比较器的例子,按照传统的写法,如下:

  Integer[]a={1,8,3,9,2,0,5};

  Arrays.sort(a,newComparator(){

  @Override

  publicintcompare(Integero1,Integero2){

  returno1-o2;

  }

  });

  Lambda表达式写法如下:

  Integer[]a={1,8,3,9,2,0,5};

  Arrays.sort(a,(o1,o2)->o1-o2);

  JDK中的函数式接口

  为了现有的类库能够直接使用Lambda表达式,Java8以前存在一些接口已经被标注为函数式接口的:

  java.lang.Runnable

  java.util.Comparator

  java.util.concurrent.Callable

  java.io.FileFilter

  java.security.PrivilegedAction

  java.beans.PropertyChangeListener

  Java8中更是新增加了一个包java.util.function,带来了常用的函数式接口:

  Function-函数:输入T输出R

  BiFunction-函数:输入T和U输出R对象

  Predicate-断言/判断:输入T输出boolean

  BiPredicate-断言/判断:输入T和U输出boolean

  Supplier-生产者:无输入,输出T

  Consumer-消费者:输入T,无输出

  BiConsumer-消费者:输入T和U无输出

  UnaryOperator-单元运算:输入T输出T

  BinaryOperator-二元运算:输入T和T输出T

  另外还对基本类型的处理增加了更加具体的函数是接口,包括:BooleanSupplier,DoubleBinaryOperator,DoubleConsumer,DoubleFunction,DoublePredicate,DoubleSupplier,DoubleToIntFunction,DoubleToLongFunction,DoubleUnaryOperator,IntBinaryOperator,IntConsumer,IntFunction,IntPredicate,IntSupplier,IntToDoubleFunction,IntToLongFunction,IntUnaryOperator,LongBinaryOperator,LongConsumer,LongFunction,LongPredicate,LongSupplier,LongToDoubleFunction,LongToIntFunction,LongUnaryOperator,ToDoubleBiFunction,ToDoubleFunction,ToIntBiFunction,ToIntFunction,ToLongBiFunction,ToLongFunction。结合上面的函数式接口,对这些基本类型的函数式接口通过类名就能一眼看出接口的作用。

  创建函数式接口

  有时候我们需要自己实现一个函数式接口,做法也很简单,首先你要保证此接口只能有一个函数操作,然后在接口类型上标注注解@FunctionalInterface即可。

  类型推导

  类型推导是Lambda表达式的基础,类型推导的过程就是Lambda表达式的编译过程。以下面的代码为例:

  FunctionstrToInt=str->Integer.parseInt(str);

  编译期间,我理解的类型推导的过程如下:

  先确定目标类型Function

  Function作为函数式接口,其方法签名为:Integerapply(Stringt)

  检测str->Integer.parseInt(str)是否与方法签名匹配(方法的参数类型、个数、顺序和返回值类型)

  如果不匹配,则报编译错误

  这里的目标类型是关键,通过目标类型获取方法签名,然后和Lambda表达式做出对比。

  方法引用

  方法引用(MethodReference)的基础同样是函数式接口,可以直接作为函数式接口的实现,与Lambda表达式有相同的作用,同样依赖于类型推导。方法引用可以看作是只调用一个方法的Lambda表达式的简化。

  方法引用的语法为:Type::methodName或者instanceName::methodName,构造函数对应的methodName为new。

  例如上面曾用到例子:

  FunctionstrToInt=str->Integer.parseInt(str);

  对应的方法引用的写法为

  FunctionstrToInt=Integer::parseInt;

  根据方法的类型,方法引用主要分为一下几种类型,构造方法引用、静态方法引用、实例上实例方法引用、类型上实例方法引用等

  构造方法引用

  语法为:Type::new。如下面的函数为了将字符串转为数组

  方法引用写法

  FunctionstrToInt=Integer::new;

  Lambda写法

  FunctionstrToInt=str->newInteger(str);

  传统写法

  FunctionstrToInt=newFunction(){

  @Override

  publicIntegerapply(Stringstr){

  returnnewInteger(str);

  }

  };

  数组构造方法引用

  语法为:Type[]::new。如下面的函数为了构造一个指定长度的字符串数组

  方法引用写法

  FunctionfixedArray=String[]::new;

  方法引用写法

  FunctionfixedArray=length->newString[length];

  传统写法

  FunctionfixedArray=newFunction(){

  @Override

  publicString[]apply(Integerlength){

  returnnewString[length];

  }

  };

  静态方法引用

  语法为:Type::new。如下面的函数同样为了将字符串转为数组

  方法引用写法

  FunctionstrToInt=Integer::parseInt;

  Lambda写法

  FunctionstrToInt=str->Integer.parseInt(str);

  传统写法

  FunctionstrToInt=newFunction(){

  @Override

  publicIntegerapply(Stringstr){

  returnInteger.parseInt(str);

  }

  };

  实例上实例方法引用

  语法为:instanceName::methodName。如下面的判断函数用来判断给定的姓名是否在列表中存在

  Listnames=Arrays.asList(newString[]{"张三","李四","王五"});

  PredicatecheckNameExists=names::contains;

  System.out.println(checkNameExists.test("张三"));

  System.out.println(checkNameExists.test("张四"));

  类型上实例方法引用

  语法为:Type::methodName。运行时引用是指上下文中的对象,如下面的函数来返回字符串的长度

  FunctioncalcStrLength=String::length;

  System.out.println(calcStrLength.apply("张三"));

  Listnames=Arrays.asList(newString[]{"zhangsan","lisi","wangwu"});

  names.stream().map(String::length).forEach(System.out::println);

  又比如下面的函数已指定的分隔符分割字符串为数组

  BiFunctionsplit=String::split;

  String[]names=split.apply("zhangsan,lisi,wangwu",",");

  System.out.println(Arrays.toString(names));

  Stream对象

  概念

  什么是Stream?这里的Stream不同于io中的InputStream和OutputStream,Stream位于包java.util.stream中,也是java8新加入的,Stream只的是一组支持串行并行聚合操作的元素,可以理解为集合或者迭代器的增强版。什么是聚合操作?

  Stream的几个特征:

  单次处理。一次处理结束后,当前Stream就关闭了。

  支持并行操作

  常见的获取Stream的方式

  从集合中获取

  Collection.stream();

  Collection.parallelStream();

  静态工厂

  Arrays.stream(array)

  Stream.of(T…)

  IntStream.range()

  这里只对Stream做简单的介绍,下面会有具体的应用。要说Stream与Lambda表达式有什么关系,其实并没有什么特别紧密的关系,只是Lambda表达式极大的方便了Stream的使用。如果没有Lambda表达式,使用Stream的过程中会产生大量的匿名类,非常别扭。

  举例

  以下的demo依赖于Employee对象,以及由Employee对象组成的List对象。

  publicclassEmployee{

  privateStringname;

  privateStringsex;

  privateintage;

  publicEmployee(Stringname,Stringsex,intage){

  super();

  this.name=name;

  this.sex=sex;

  this.age=age;

  }

  publicStringgetName(){

  returnname;

  }

  publicStringgetSex(){

  returnsex;

  }

  publicintgetAge(){

  returnage;

  }

  @Override

  publicStringtoString(){

  StringBuilderbuilder=newStringBuilder();

  builder.append("Employee{name=").append(name).append(",sex=").append

我的位置: 首页 >> Java Lambda 表达式学习笔记

2018-09-29

来源:


 

在线答疑更多++

热门专题更多++

广东省佛山市禅城区祖庙路1号北大青鸟华大校区

地址:广东省佛山市禅城区祖庙路1号富荣大厦4楼

电话:4006-989-522  0757-88726000

网址: www.foshanbdqn.com

佛山校区乘车路线:旋宫酒店站、亲仁路站、松风路站、莲花站

北大青鸟华大校区公众平台

佛山北大青鸟