佛山做网站建设/南宁网站推广公司
概述:
- 函数式接口:有且仅有一个抽象方法的接口
- 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就是函数式接口,把它作为参数传入线程中启动
package com.testFunctionalInterface;/*** @author 林高禄* @create 2020-06-03-15:44*/
public class Demo1 {public static void main(String[] args) {// 匿名内部类new Thread(new Runnable() {@Overridepublic 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就是函数式接口
按学生的年龄排序
- 排序前打印
- 学生类实现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>() {@Overridepublic int compare(Student s1,Student s2) {return s2.getAge()-s1.getAge();}};*/// Lambda表达式return (s1,s2)->s2.getAge()-s1.getAge();}
}
class Student implements Comparable<Student>{@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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>() {@Overridepublic 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>() {@Overridepublic 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);// 方法引用1System.out.println("------------方法引用1-------------");Consumer<String> consumer2 = System.out::println;consumer2.accept(s1.getName()+"\n"+new StringBuilder(s1.getName()).reverse());// 方法引用2System.out.println("-------------方法引用2------------");consumer = ConsumerDemo1::printStudentName;consumer.accept(s1);// 调用方法1System.out.println("-------------调用方法1------------");printStudentName(s1,s-> System.out.println(s.getName()),s-> System.out.println(new StringBuilder(s.getName()).reverse()));// 调用方法2System.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>() {@Overridepublic boolean test(Student student) {return student.getAge()>30;}};System.out.println(predicate.test(student));// 判断学生是否小于等于30岁System.out.println("判断学生是否小于等于30岁");// 这里用了negate(),返回一个逻辑的否定,对应逻辑非,所以输出是trueSystem.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流