> 文章列表 > Java8函数式编程(Lambda表达式,Stream流,Optional)

Java8函数式编程(Lambda表达式,Stream流,Optional)

Java8函数式编程(Lambda表达式,Stream流,Optional)

一.函数式编程思想

  • 面向对象思想主要是关注对象能完成什么事情,函数式编程思想就像函数式,主要是针对数据操作;
  • 代码简洁容易理解,方便于并发编程,不需要过分关注线程安全问题

二.lambda表达式

1.概念

lambda表达式是Java8的一个新特性,从本质上来讲是一个匿名函数可以使用这个匿名函数实现接口中的方法,并且非常简洁。

通常来讲,使用 lambda表达式 是为了简化接口实现的。关于接口实现,可以有很多种方式来实现。例如设计接口的实现类、使用匿名内部类。但是lambda表达式比这两种方式都简单。可以把lambda看成JDK8中的一个语法糖。

2.Lambda表达式对接口的要求

并不是所有的接口都可以使用lambda表达式来简洁实现的。lambda表达式毕竟只是一个匿名方法。当实现的接口中的方法过多或者过少的时候,lambda表达式都是不适用的。

lambda表达式只能实现函数式接口。即一个接口中,要求实现类必须实现的抽象方法,有且只有一个,这样的接口就是函数式接口。

3.Lambda表达式的语法

(参数)->{方法体}
  • 参数部分︰方法的参数列表,要求和实现的接口中的方法参数部分一致,包括参数的数量和类型
  • 方法体部分∶方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回。
  • ->:分隔参数部分和方法体部分。 

  • 因为接口中规定了方法参数类型,所以实际写lambda表达式的时候可以省略 参数类型。如果只有一个参数,可以省略()
  • 另外如果方法体里只有一个语句,那么可以省略掉 {},如果该语句是return语句,需要把return也省略掉

省略如下:

4.函数引用

lambda表达式是为了简化接口的实现的。在lambda表达式中,不应该出现比较复杂的逻辑。如果在lambda表达式中出现了过于复杂的逻辑,会对程序的可读性造成非常大的影响。

如果在lambda表达式中需要处理的逻辑比较复杂,一般情况会单独的写一个方法。在lambda表达式中直接引用这个方法即可。或者,在有些情况下,我们需要在lambda表达式中实现的逻辑,在另外一个地方已经写好了。此时我们就不需要再单独写一遍,只需要直接引用这个已经存在的方法即可。

函数引用:引用一个已经存在的方法,使其替代lambda表达式完成接口的实现。

4.1引用一个静态方法

接口依然是上面的MultipleParametersReturn,引用如下 

需要注意的是引用的 方法的参数返回值类型 要与 接口 对应

4.2引用一个非静态方法

4.3引用构造方法 

Person类

public class Person {private Integer age;private String name;public Person() {}public Person(Integer age) {this.age = age;}public Person(Integer age, String name) {this.age = age;this.name = name;}@Overridepublic String toString() {return "Person{" +"age=" + age +", name='" + name + '\\'' +'}';}
}

PersonWithNoParam接口

@FunctionalInterface
public interface PersonWithNoParam {Person getPersonWithNoParam();
}

PersonWithOneParam接口

@FunctionalInterface
public interface PersonWithOneParam {Person getPersonWithOneParam(Integer age);
}

PersonWithDoubleParams接口

@FunctionalInterface
public interface PersonWithDoubleParams {Person getPersonWithDoubleParams(Integer age,String name);
}

Demo

public class Demo1 {public static void main(String[] args) {//lambda表达式实现PersonWithNoParam接口PersonWithNoParam t1=Person::new;//无参构造函数引用System.out.println(t1.getPersonWithNoParam());//lambda表达式实现PersonWithOneParam接口PersonWithOneParam t2=Person::new;//单参构造函数引用System.out.println(t2.getPersonWithOneParam(10));//lambda表达式实现PersonWithDoubleParams接口PersonWithDoubleParams t3=Person::new;//多参构造函数引用System.out.println(t3.getPersonWithDoubleParams(10,"小王"));}}

lambda表达式会自动根据 方法的参数类型 匹配Person类中对应的构造方法

5.特殊的函数引用

如果在使用lambda表达式实现某些接口的时候,lambda表达式的参数列表中包含了某一个对象,此时方法体中,直接使用这个对象调用它的某一个方法就可以完成整体的逻辑并且其他的参数可以作为调用方法的参数。此时,可以对这种实现进行简化。

Person类

public class Person {private Integer age;private String name;public Person() {}public Person(Integer age) {this.age = age;}public Person(Integer age, String name) {this.age = age;this.name = name;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"age=" + age +", name='" + name + '\\'' +'}';}
}

PersonSpecial接口

public interface PersonSpecial {void setAge (Person person,Integer age);
}

Demo2

public class Demo2 {public static void main(String[] args) {//简化前PersonSpecial t=(person,age)->person.setAge(age);//用对象的特殊引用简化后PersonSpecial t2=Person::setAge;Person person = new Person();t2.setAge(person,13);System.out.println(person);}
}

6.闭包

如果某一个局部变量被用在了某一个代码块中,这其实就形成了对这个局部变量的包围,我们称"闭包"。这个时候这个变量不允许被改变,即final

如下编译器会报错:

如果使用的是全局变量则不会出现闭包问题 

三.Stream流

Java8的 使用的是函数式编程模式,可以被用来对集合或数组进行链状流式的操作。有别于IO流,这里是针对集合操作数据的流