0
点赞
收藏
分享

微信扫一扫

函数式接口(Lambda表达式,@FunctionalInterface)

Java旺 2022-02-25 阅读 66



概述:

  • 函数式接口:有且仅有一个抽象方法的接口




  • Java中函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用Lambda使用的接口,只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导
  • 不清楚Lambda表达式的可以看这个



我们先看一个例子,demo



Interface1:接口1

Demo:示例demo 


package com.testFunctionalInterface;

/**
* @author 林高禄
* @create 2020-06-03-15:31
*/
public class Demo {
public static void main(String[] args) {
useInterface1("林高禄",s-> System.out.println(s));
}
private static void useInterface1(String s,Interface1 i){
i.method(s);
}
}

interface Interface1{
void method(String s);
}


运行输出:

林高禄



接口Interface1,只有一个抽象方法,并且能用Lambda表达式体现,那么我们就说接口Interface1是函数式接口,那么有什么标志能体现Interface1是函数式接口呢,如果别人不知道,在Interface1里面再加一个抽象方法,那么Interface1不会报错,但是使用的Lambda就会报错了,所以我们要用一个标志标明这个接口就是函数式接口,不能在加其他的抽象方法了,这个标志就是

@FunctionalInterface


如何检测一个接口是不是函数式接口呢?


  • @FunctionalInterface
  • 放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
  • 加了@FunctionalInterface的接口,有且仅有一个抽象方法,才编译通过。



那么我们的示例修改后的代码为


package com.testFunctionalInterface;

/**
* @author 林高禄
* @create 2020-06-03-15:31
*/
public class Demo {
public static void main(String[] args) {
useInterface1("林高禄",s-> System.out.println(s));
}
private static void useInterface1(String s,Interface1 i){
i.method(s);
}
}

@FunctionalInterface
interface Interface1{
void method(String s);
}


函数式接口作为参数



 接口Runnable就是函数式接口,把它作为参数传入线程中启动

函数式接口(Lambda表达式,@FunctionalInterface)_lambda表达式



package com.testFunctionalInterface;

/**
* @author 林高禄
* @create 2020-06-03-15:44
*/
public class Demo1 {
public static void main(String[] args) {
// 匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程"+Thread.currentThread().getName()+"启动了!");
}
}).start();
// Lambda表达式
new Thread(()-> System.out.println("线程"+Thread.currentThread().getName()+"启动了!")).start();
Runnable runnable = () -> System.out.println("线程" + Thread.currentThread().getName() + "启动了!");
new Thread(runnable).start();
}
}


运行输出:

线程Thread-0启动了!
线程Thread-1启动了!
线程Thread-2启动了!



函数式接口作为返回值



Comparator就是函数式接口

函数式接口(Lambda表达式,@FunctionalInterface)_函数式接口_02

按学生的年龄排序


  • 排序前打印
  • 学生类实现Comparable接口,重写排序方法compareTo进行年龄排序后打印
  • 自写获取排序进行年龄排序后打印
  • 自写获取排序进行颜值排序后打印


package com.testFunctionalInterface;

import java.util.*;

/**
* @author 林高禄
* @create 2020-06-03-15:56
*/
public class Demo2 {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
Student s1 = new Student("林高禄",27,90);
Student s2 = new Student("吴忠威",31,99);
Student s3 = new Student("徐辉强",25,97);
list.add(s1);
list.add(s2);
list.add(s3);
System.out.println("排序前"+list);
Collections.sort(list);
System.out.println("类自排序,年龄排序后"+list);
Collections.sort(list,getComparator());
System.out.println("获取返回值排序,年龄排序后"+list);
Collections.sort(list,(student1,student2)->student2.getYanzhi()-student1.getYanzhi());
System.out.println("获取返回值排序,颜值排序后"+list);

}

private static Comparator<Student> getComparator(){
// 匿名内部类
/* return new Comparator<Student>() {
@Override
public int compare(Student s1,Student s2) {
return s2.getAge()-s1.getAge();
}
};*/
// Lambda表达式
return (s1,s2)->s2.getAge()-s1.getAge();
}
}
class Student implements Comparable<Student>{
@Override
public int compareTo(Student s) {
return this.getAge()-s.getAge();
}

private String name;
private int age;
private int yanzhi;

public Student(String name, int age, int yanzhi) {
this.name = name;
this.age = age;
this.yanzhi = yanzhi;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public int getYanzhi() {
return yanzhi;
}

public void setYanzhi(int yanzhi) {
this.yanzhi = yanzhi;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", yanzhi=" + yanzhi +
'}';
}
}


运行输出:

排序前[Student{name='林高禄', age=27, yanzhi=90}, Student{name='吴忠威', age=31, yanzhi=99}, Student{name='徐辉强', age=25, yanzhi=97}]
类自排序,年龄排序后[Student{name='徐辉强', age=25, yanzhi=97}, Student{name='林高禄', age=27, yanzhi=90}, Student{name='吴忠威', age=31, yanzhi=99}]
获取返回值排序,年龄排序后[Student{name='吴忠威', age=31, yanzhi=99}, Student{name='林高禄', age=27, yanzhi=90}, Student{name='徐辉强', age=25, yanzhi=97}]
获取返回值排序,颜值排序后[Student{name='吴忠威', age=31, yanzhi=99}, Student{name='徐辉强', age=25, yanzhi=97}, Student{name='林高禄', age=27, yanzhi=90}]



从最后一个排序看出,我们用Lambda表达式写排序简直就是太方便了



常用的函数式接口(Java8)


  • Supplier接口(供给型)
  • Consumer接口(消费型)
  • Predicate接口(谓词型)
  • Function接口(功能型)



我们用学生类来做例子,学生类Student主要有姓名,年龄 


package com.testFunctionalInterface;

/**
* @author 林高禄
* @create 2020-06-04-9:11
*/
public class Student {
private String name;
private int age;

public Student(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}



Supplier接口(供给型)

Supplier<T>:包含一个无参的方法


  • T get():获得结果
  • 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
  • Supplier<T>接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用



例子1:获取年龄大于等于28岁的第一个学生


package com.testFunctionalInterface;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

/**
* @author 林高禄
* @create 2020-06-04-9:08
*/
public class SupplierDemo1 {
public static void main(String[] args) {
Student s1 = new Student("林高禄",27);
Student s2 = new Student("吴忠威",31);
Student s3 = new Student("徐辉强",28);
List<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
// Lambda表达式获取Supplier对象
Supplier<Student> supplier = ()->{
Student returnStudent = null;
for(Student s:list){
if(s.getAge()>=28){
returnStudent = s;
break;
}
}
return returnStudent;
};
// 直接获取
System.out.println(supplier.get().toString());
System.out.println("----------------------------");
// 传入方法获取
Student student = getStudent(supplier);
System.out.println(student.toString());
}

private static Student getStudent(Supplier<Student> s){
return s.get();
}
}


运行输出:

Student{name='吴忠威', age=31}
----------------------------
Student{name='吴忠威', age=31}



例子2:获取随机数,分别用匿名内部类,Lambda表达式和方法引用获取


package com.testFunctionalInterface;

import java.util.Random;
import java.util.function.Supplier;

/**
* @author 林高禄
* @create 2020-06-04-9:20
*/
public class SupplierDemo2 {
public static void main(String[] args) {
// 匿名内部类
Supplier<Integer> supplier = new Supplier<Integer>() {
@Override
public Integer get() {
return new Random().nextInt();
}
};
System.out.println(supplier.get());
System.out.println("-------------------------------");
// Lambda表达式
supplier = () -> new Random().nextInt();
System.out.println(supplier.get());
System.out.println("-------------------------------");
// 方法引用
Supplier<Random> supplier2 = Random::new;
System.out.println(supplier2.get().nextInt());
}

}


运行输出:

-729626870
-------------------------------
809183650
-------------------------------
-803077133 



Consumer接口(消费型)

Consumer<T>:包含两个方法


  • void accept(T t):对给定的参数执行操作
  • default Consumer<T> addThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作
  • Consumer<T>接口也被称为消费型接口,它消费的数据的数据类型由泛型决定



例子1:消费一个学生,并且输出学生的名字,反转再输出一次



代码说明:


  • 匿名内部类消费
  • Lambda表达式消费
  • 方法引用1消费
  • 方法引用2消费
  • 调用方法1消费
  • 调用方法2消费


package com.testFunctionalInterface;

import java.util.function.Consumer;

/**
* @author 林高禄
* @create 2020-06-04-9:08
*/
public class ConsumerDemo1 {
public static void main(String[] args) {
Student s1 = new Student("林高禄",27);
// 匿名内部类
System.out.println("------------匿名内部类-------------");
Consumer<Student> consumer = new Consumer<Student>() {
@Override
public void accept(Student student) {
System.out.println(student.getName());
System.out.println(new StringBuilder(student.getName()).reverse());
}
};
consumer.accept(s1);
// Lambda表达式
System.out.println("------------Lambda表达式-------------");
consumer = s-> System.out.println(s.getName()+"\n"+new StringBuilder(s.getName()).reverse());
consumer.accept(s1);
// 方法引用1
System.out.println("------------方法引用1-------------");
Consumer<String> consumer2 = System.out::println;
consumer2.accept(s1.getName()+"\n"+new StringBuilder(s1.getName()).reverse());
// 方法引用2
System.out.println("-------------方法引用2------------");
consumer = ConsumerDemo1::printStudentName;
consumer.accept(s1);
// 调用方法1
System.out.println("-------------调用方法1------------");
printStudentName(s1,s-> System.out.println(s.getName()),s-> System.out.println(new StringBuilder(s.getName()).reverse()));
// 调用方法2
System.out.println("-------------调用方法2------------");
printStudentName2(s1,s-> System.out.println(s.getName()),s-> System.out.println(new StringBuilder(s.getName()).reverse()));
}

private static void printStudentName(Student s){
System.out.println(s.getName()+"\n"+new StringBuilder(s.getName()).reverse());
}

private static void printStudentName(Student s,Consumer<Student> consumer1,Consumer<Student> consumer2){
consumer1.accept(s);
consumer2.accept(s);
}
private static void printStudentName2(Student s,Consumer<Student> consumer1,Consumer<Student> consumer2){
consumer1.andThen(consumer2).accept(s);
}
}


运行输出:

------------匿名内部类-------------
林高禄
禄高林
------------Lambda表达式-------------
林高禄
禄高林
------------方法引用1-------------
林高禄
禄高林
-------------方法引用2------------
林高禄
禄高林
-------------调用方法1------------
林高禄
禄高林
-------------调用方法2------------
林高禄
禄高林



Predicate接口(谓词型)

Predicate<T>:常用的四个方法


  • boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
  • default Predicate<T> negate():返回一个逻辑的否定,对应逻辑非
  • default Predicate<T> and(Predicate other):返回一个组合判断,对应短语与
  • default Predicate<T> or(Predicate other):返回一个组合判断,对应短语或
  • Predicate<T> 接口通常用于判断参数是否满足指定的条件,起判断作用



例子,判断学生是否满足各种条件


package com.testFunctionalInterface;

import java.util.function.Predicate;

/**
* @author 林高禄
* @create 2020-06-04-11:07
*/
public class PredicateDemo {
public static void main(String[] args) {
Student student = new Student("林高禄",27);
System.out.println("学生信息:"+student);
// 判断学生是否姓名是否以"林"字开头
System.out.println("判断学生是否姓名是否以\"林\"字开头");
Predicate<Student> predicate = s->s.getName().startsWith("林");
System.out.println(predicate.test(student));
// 判断学生是否超过30岁
System.out.println("判断学生是否超过30岁");
predicate = new Predicate<Student>() {
@Override
public boolean test(Student student) {
return student.getAge()>30;
}
};
System.out.println(predicate.test(student));
// 判断学生是否小于等于30岁
System.out.println("判断学生是否小于等于30岁");
// 这里用了negate(),返回一个逻辑的否定,对应逻辑非,所以输出是true
System.out.println(predicate.negate().test(student));
// 判断学生是否以"林"字开头,或者超过30岁
System.out.println("判断学生是否以\"林\"字开头,或者超过30岁");
predicate = s->s.getName().startsWith("林");
Predicate<Student> predicate2 = s->s.getAge()>30;
System.out.println(predicate.or(predicate2).test(student));
// 判断学生是否以"林"字开头,并且超过30岁
System.out.println("判断学生是否以\"林\"字开头,并且超过30岁");
System.out.println(predicate.and(predicate2).test(student));
}
}


运行输出:

学生信息:Student{name='林高禄', age=27}
判断学生是否姓名是否以"林"字开头
true
判断学生是否超过30岁
false
判断学生是否小于等于30岁
true
判断学生是否以"林"字开头,或者超过30岁
true
判断学生是否以"林"字开头,并且超过30岁
false



Function接口(功能型)

Function<T,R>:常用的两个方法


  • R apply(T t):将此函数应用于给定的参数
  • default<V> Function andThen(Functiona after):返回一个函数组合,首先将该函数应用于输入,然后将after函数应用于结果
  • default<V> Function compose(Functiona before):返回一个函数组合,首先将before函数应用于输入,然后将此函数应用于结果



例子:处理学生类


package com.testFunctionalInterface;

import java.util.function.Function;

/**
* @author 林高禄
* @create 2020-06-04-11:34
*/
public class FunctionDemo {
public static void main(String[] args) {
Student student = new Student("林高禄",27);
// 求出学生的姓名长度
Function<Student,Integer> function = s->s.getName().length();
System.out.println(function.apply(student));
// 把学生的姓名后面加上"**"
Function<Student,String> function2 = Student::getName;
Function<String,String> function3 = s->s+"**";
System.out.println(function2.andThen(function3).apply(student));
//注意比较和上面的区别,一前一后
System.out.println(function3.compose(function2).apply(student));
}
}


运行输出:

3
林高禄**
林高禄**



注意andThen和compose的顺序,到这里常用的函数式接口就介绍完了



函数式接口的进阶用法,Stream流



举报

相关推荐

0 条评论