java8新特性学习-函数式编程(lambda表达式/Consumer/Supplier/Function/Predicate)
- 1.什么是lambda表达式
- 2.lambda表达式基础语法
- 3.四个内置函数式接口
- 4.方法的引用以及构造器引用
1.什么是lambda表达式
- 说明lambda表达式之前我们先要知道什么是函数式接口。
- 函数式接口有两个特征:
- ①可以用@FunctionalInterfae注解修饰。
- ②接口内只能有一个抽象方法。
- 那么这样的接口我们可以称为函数式接口。
- lambda表达式就是一种匿名函数,即上面提到的唯一的抽象方法的实现。
JDK1.8之前我们怎么做?
- 我们有一个Emplyee员工类,属性如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
// 姓名
private String name;
// 年龄
private Integer age;
// 工资
private Double salary;
// 地区
private String district;
}
- 然后创建我们的初始化代码
List<Employee> employees = new ArrayList<>();
@Before
public void before() {
employees.add(new Employee("张", 34, 1111.11, "北京"));
employees.add(new Employee("王", 23, 2222.22, "上海"));
employees.add(new Employee("李", 43, 6666.66, "广州"));
employees.add(new Employee("赵", 66, 888.6, "北京"));
}
- 需求:按照工资从小到大排序并输出
- 我们在学习函数式接口之前这么写
@Test
public void test1() {
Comparator<Employee> comparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
return Double.compare(o1.getSalary(), o2.getSalary());
}
};
Collections.sort(employees, comparator);
for (Employee employee : employees) {
System.out.println(employee);
}
}
- 输出:
Employee(name=赵, age=66, salary=888.6, district=北京)
Employee(name=张, age=34, salary=1111.11, district=北京)
Employee(name=王, age=23, salary=2222.22, district=上海)
Employee(name=李, age=43, salary=6666.66, district=广州)
- 我们看下Comparator在jdk中的定义
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
- 符合函数式接口的定义,那么我们可以这么写
@Test
public void test2() {
Collections.sort(employees, (o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary()));
for (Employee employee : employees) {
System.out.println(employee);
}
}
- 输出:
Employee(name=赵, age=66, salary=888.6, district=北京)
Employee(name=张, age=34, salary=1111.11, district=北京)
Employee(name=王, age=23, salary=2222.22, district=上海)
Employee(name=李, age=43, salary=6666.66, district=广州)
- (o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary()) 这一段代码被称为lambda表达式,也就是Comparator中的抽象方法compare的实现。
- 优点:
- ①.代码简洁
- ②.将实现延后
- ③.当我们再需要对Employee的其他属性做比较时,无需再写一个Comparator,可以直接使用lambda表达式。
2.lambda表达式基础语法
- lambda表达式由三部分组成:
- ①.参数,即操作符->左侧的部分,没有参数时可以写成(),另外由于有类型推断,所以这里的参数类型可以省略
- ②.操作符,即->
- ③.函数体,即操作符->右侧的部分,这里当有多条语句时,用大括号{}括起来,当只有一条语句时大括号{}可以省略,并且有返回值时关键字return可以省略。
3.四个内置函数式接口
由于使用lambda表达式进行编程,函数式接口必不可少,那么在JDK1.8中,已经给我们定义好了一些拿来即用的接口,主要有以下四个接口以及他们的子接口。
- ①.Consumer接口
消费型接口,他的定义如下
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
- 我们主要是实现其中的accept方法,由于是消费型的接口他接收一个参数,没有返回值。
- 需求:打印所有Employee的值
@Test
public void test3() {
Consumer<Employee> consumer = (x) -> System.out.println(x);
for (Employee employee : employees) {
consumer.accept(employee);
}
}
- 输出:
Employee(name=张, age=34, salary=1111.11, district=北京)
Employee(name=王, age=23, salary=2222.22, district=上海)
Employee(name=李, age=43, salary=6666.66, district=广州)
Employee(name=赵, age=66, salary=888.6, district=北京)
- ②.Supplier接口
供应型接口,他的定义如下:
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
- 我们主要是实现其中的get方法,他不需要传入任何参数,有返回值。
- 需求:返回Employee的第一个对象。
@Test
public void test4() {
Supplier<Employee> supplier = () -> employees.get(0);
System.out.println(supplier.get());
}
- 输出:
Employee(name=张, age=34, salary=1111.11, district=北京)
- ③.Function接口
函数式接口,他的定义如下:
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* Returns a function that always returns its input argument.
*
* @param <T> the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static <T> Function<T, T> identity() {
return t -> t;
}
- 我们主要实现apply方法,他接收一个参数,有返回值。
- 需求:返回指定index的Employee。
@Test
public void test5() {
Function<Integer, Employee> function = (x) -> employees.get(x);
System.out.println(function.apply(2));
}
- 输出:
Employee(name=李, age=43, salary=6666.66, district=广州)
- ④.Predicate接口
判断型接口,他的定义如下:
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ANDed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* AND of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*
* @return a predicate that represents the logical negation of this
* predicate
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ORed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* OR of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param <T> the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
- 我们主要实现test方法,他有一个参数,有一个boolean行的返回值。
- 需求:打印出年龄大于50的员工。
@Test
public void test6() {
Predicate<Employee> predicate = (x) -> x.getAge() > 50;
for (Employee employee : employees) {
if (predicate.test(employee)) {
System.out.println(employee);
}
}
}
- 输出:
Employee(name=赵, age=66, salary=888.6, district=北京)
除了这四个大类接口,jdk为我们定义了更多类型的函数式接口,他们都在java.util.function包下。
4.方法的引用以及构造器引用
- 方法引用:
- ①.对象::实例方法名
- 例子:
@Test
public void test7() {
Consumer<String> consumer = System.out::println;
consumer.accept("dddddd");
}
-
System.out::println就是对象::实例方法名的方式。
-
②.类名::静态方法名
@Test
public void test8() {
Comparator<Integer> comparator = Integer::compare;
System.out.println(comparator.compare(100, 200));
}
- Integer::compare compare是Integer类的一个静态方法
- ③.类名::实例方法名
@Test
public void test9() {
Comparator<Integer> comparator = Integer::compareTo;
System.out.println(comparator.compare(100, 200));
}
- compareTo 是Integer的一个实例方法,正常来说我们应该写成
Comparator comparator = (x, y) -> x.compareTo(y);
但是可见我们是可以写成类::实例方法的形式来使用的,但是这里需要满足一个条件,即x必须是实例方法(compareTo)的调用者,而y是实例方法的参数,这时可以简化为类::实例方法的形式。 - 构造器引用:
- 构造器引用没啥好说的,就是类名::new这种方式。
- 需要注意的是调用构造器的参数列表要与抽象方法的列表参数保持一致