如果在一个接口中只声明了一个抽象方法,则此接口就被称为函数式接口(该接口可以包含其他非抽象方法)
@FunctionalInterface注解
可以验证该接口是否为函数式接口(如果接口中有多个抽象方法编译器会报错,javadoc生成的文档时 会保留该注解随着Python,Scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,所以Java8不但可以支持OOP还可以支持OOF(面向函数编程)
在java.util.function
包下定义了Java 8 的丰富的函数式接口
四大核心函数式接口
函数式接口 | 称谓 | 参数类型 | 用途 |
---|---|---|---|
Consumer |
消费型接口 | T | 对类型为T的对象应用操作,包含方法: void accept(T t) |
Supplier |
供给型接口 | 无 | 返回类型为T的对象,包含方法:T get() |
Function |
函数型接口 | T | 对类型为T的对象应用操作并返回结果R类型的对象,包含方法:R apply(T t) |
Predicate |
判断型接口 | T | 确定类型为T的对象是否满足某约束并返回 boolean 值,包含方法:boolean test(T t) |
消费型接口: 抽象方法有形参但是返回值类型是void
接口名 | 抽象方法 | 描述 |
---|---|---|
BiConsumer |
void accept(T t, U u) | 接收两个对象用于完成功能 |
DoubleConsumer | void accept(double value) | 接收一个double值 |
IntConsumer | void accept(int value) | 接收一个int值 |
LongConsumer | void accept(long value) | 接收一个long值 |
ObjDoubleConsumer | void accept(T t, double value) | 接收一个对象和一个double值 |
ObjIntConsumer | void accept(T t, int value) | 接收一个对象和一个int值 |
ObjLongConsumer | void accept(T t, long value) | 接收一个对象和一个long值 |
供给型接口: 抽象方法无参但是有返回值
接口名 | 抽象方法 | 描述 |
---|---|---|
BooleanSupplier | boolean getAsBoolean() | 返回一个boolean值 |
DoubleSupplier | double getAsDouble() | 返回一个double值 |
IntSupplier | int getAsInt() | 返回一个int值 |
LongSupplier | long getAsLong() | 返回一个long值 |
函数型接口: 抽象方法既有参数又有返回值
接口名 | 抽象方法 | 描述 |
---|---|---|
UnaryOperator | T apply(T t) | 接收一个T类型对象,返回一个T类型对象结果 |
DoubleFunction | R apply(double value) | 接收一个double值,返回一个R类型对象 |
IntFunction | R apply(int value) | 接收一个int值,返回一个R类型对象 |
LongFunction | R apply(long value) | 接收一个long值,返回一个R类型对象 |
ToDoubleFunction | double applyAsDouble(T value) | 接收一个T类型对象,返回一个double |
ToIntFunction | int applyAsInt(T value) | 接收一个T类型对象,返回一个int |
ToLongFunction | long applyAsLong(T value) | 接收一个T类型对象,返回一个long |
DoubleToIntFunction | int applyAsInt(double value) | 接收一个double值,返回一个int结果 |
DoubleToLongFunction | long applyAsLong(double value) | 接收一个double值,返回一个long结果 |
IntToDoubleFunction | double applyAsDouble(int value) | 接收一个int值,返回一个double结果 |
IntToLongFunction | long applyAsLong(int value) | 接收一个int值,返回一个long结果 |
LongToDoubleFunction | double applyAsDouble(long value) | 接收一个long值,返回一个double结果 |
LongToIntFunction | int applyAsInt(long value) | 接收一个long值,返回一个int结果 |
DoubleUnaryOperator | double applyAsDouble(double operand) | 接收一个double值,返回一个double |
IntUnaryOperator | int applyAsInt(int operand) | 接收一个int值,返回一个int结果 |
LongUnaryOperator | long applyAsLong(long operand) | 接收一个long值,返回一个long结果 |
BiFunction |
R apply(T t, U u) | 接收一个T类型和一个U类型对象,返回一个R类型对象结果 |
BinaryOperator | T apply(T t, T u) | 接收两个T类型对象,返回一个T类型对象结果 |
ToDoubleBiFunction |
double applyAsDouble(T t, U u) | 接收一个T类型和一个U类型对象,返回一个double |
ToIntBiFunction |
int applyAsInt(T t, U u) | 接收一个T类型和一个U类型对象,返回一个int |
ToLongBiFunction |
long applyAsLong(T t, U u) | 接收一个T类型和一个U类型对象,返回一个long |
DoubleBinaryOperator | double applyAsDouble(double left, double right) | 接收两个double值,返回一个double结果 |
IntBinaryOperator | int applyAsInt(int left, int right) | 接收两个int值,返回一个int结果 |
LongBinaryOperator | long applyAsLong(long left, long right) | 接收两个long值,返回一个long结果 |
判断型接口: 抽象方法特点有参但是返回值类型是boolean结果
接口名 | 抽象方法 | 描述 |
---|---|---|
BiPredicate |
boolean test(T t, U u) | 接收两个对象 |
DoublePredicate | boolean test(double value) | 接收一个double值 |
IntPredicate | boolean test(int value) | 接收一个int值 |
LongPredicate | boolean test(long value) | 接收一个long值 |
消费型接口使用举例
public void happyTime(double money, Consumer<Double> consumer) {
consumer.accept(money);
}
@Test
public void test04() {
// 传统写法
happyTime(1241, new Consumer<Double>() {
@Override
public void accept(Double money) {
System.out.println("突然想回一趟成都了,机票花费" + money);
}
});
System.out.println("------------------------");
// Lambda表达式
happyTime(648, money -> System.out.println("学习太累了,奖励自己一发十连,花费" + money));
}
断定型接口使用举例: 根据Predicate的方法给定的规则,过滤集合中的字符串
public List<String> filterString(List<String> strings, Predicate<String> predicate) {
ArrayList<String> res = new ArrayList<>();
for (String string : strings) {
if (predicate.test(string))
res.add(string);
}
return res;
}
@Test
public void test05() {
List<String> strings = Arrays.asList("东京", "西京", "南京", "北京", "天津", "中京");
// 传统写法
List<String> list = filterString(strings, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(list);
System.out.println("------------------------");
// Lambda表达式
List<String> res = filterString(strings, s -> s.contains("京"));
System.out.println(res);
}
当需要启动一个线程去完成任务时,通常会通过java.lang.Runnable
接口来定义任务内容并使用java.lang.Thread
类来启动该线程
Thread
类需要Runnable
接口作为参数,其中的抽象run
方法是用来指定线程任务内容的核心run
的方法体需要创建Runnable
接口的实现类,为了省去定义一个RunnableImpl
实现类的麻烦需要使用匿名内部类run
方法,所以方法名称、方法参数、方法返回值都需要再写一遍且不能写错, 而实际上只有方法体才是关键所在public class UseFunctionalProgramming {
public static void main(String[] args) {
// new 接口(){实现类}
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程任务执行!");
}
}).start(); // 启动线程
}
}
我们可以通过Lambda表达式创建函数式接口的实现类对象代替匿名实现类,Lambda表达式是可以简化函数式接口的变量或形参赋值的语法
new Thread(() -> {
System.out.println("多线程任务执行!");
}).start();
Lambda表达式的语法格式如(o1,o2) -> Integer.compare(o1,o2)
->
:lambda操作符或箭头操作符->左边(函数式接口中抽象方法的形参列表)
:因为有"类型推断"机制形参的数据类型都可以省略,如果只有一个参数,参数的小括号可以省略->右边{函数式接口中抽象方法的方法体}
: 当Lambda体只有一条语句时,return与{}可以省略(return不能单独出现)语法格式一: 函数式接口的抽象方法无参无返回值
@Test
public void test01(){
// 传统写法
Runnable runnable01 = new Runnable() {
@Override
public void run() {
System.out.println("你 的 城 市 好 像 不 欢 迎 我");
}
};
runnable01.run();
System.out.println("-------------------------");
// Lambda表达式
Runnable runnable02 = () -> {
System.out.println("所 以 我 只 好 转 身 离 开 了");
};
runnable02.run();
}
语法格式二: 函数式接口的抽象方法有一个参数但是没有返回值
@Test
public void test03(){
//1. 传统写法
Consumer<String> consumer01 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer01.accept("其实我存过你照片 也研究过你的星座");
System.out.println("-------------------------");
// Lambda表达式
Consumer<String> consumer02 = (String s) -> {
System.out.println(s);
};
consumer02.accept("你喜欢的歌我也会去听 你喜欢的事物我也会想去了解");
}
语法格式三: 函数式接口抽象方法中形参列表的数据类型可以省略,编译器可由"类型推断"机制得出(由声明变量的泛型类型推断得出)
@Test
public void test() {
//类型推断1
ArrayList<String> list = new ArrayList<>();
//类型推断2
int[] arr = {1, 2, 3};
}
@Test
public void test04(){
// 传统写法
Consumer<String> consumer01 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer01.accept("我远比表面上更喜欢你");
System.out.println("-------------------------");
// Lambda表达式
Consumer<String> consumer02 = (s) -> {
System.out.println(s);
};
consumer02.accept("但我没有说");
}
语法格式四: 函数式接口的抽象方法只有一个参数,参数的小括号可以省略
@Test
public void test04(){
// 传统写法
Consumer<String> consumer01 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer01.accept("我远比表面上更喜欢你");
System.out.println("-------------------------");
// Lambda表达式
Consumer<String> consumer02 = s -> {
System.out.println(s);
};
consumer02.accept("但我没有说");
}
语法格式五: 接口的抽象方法有两个或以上参数,方法有返回值, 方法体有多条执行语句
@Test
public void test02() {
// 传统的写法
Comparator<Integer> comparator01 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(comparator01.compare(95, 27));
System.out.println("-------------------------");
// Lambda表达式
Comparator<Integer> comparator02 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(comparator02.compare(12, 21));
}
语法格式六: 当Lambda体只有一条语句时(可能是return语句),return与{}可以省略(return不能单独出现)
public void test02() {
// 传统写法
Comparator<Integer> comparator01 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println(comparator01.compare(95, 27));
System.out.println("-------------------------");
// Lambda表达式
Comparator<Integer> comparator02 = (o1, o2) -> o1.compareTo(o2);
System.out.println(comparator02.compare(12, 21));
}
当要传递给Lambda体的操作已经有实现的方法了可以使用方法引用,方法引用和构造器引用就是为了简化Lambda表达式
语法糖(Syntactic sugar)也译为糖衣语法是指计算机语言中添加的某种对语言的功能没有影响但是更方便程序员使用的语法
方法引用格式: 使用方法引用操作符 “::
” 将类或对象与方法名分隔开来,要求Lambda体只有一句语句并且是调用一个对象/类的方法
对象::实例方法名
类::静态方法名
类::实例方法名
先写一个Employee实体类
public class Employee {
private String name;
private Integer id;
// get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
// 构造方法
public Employee() {
}
public Employee(Integer id) {
this.id = id;
this.name = name;
}
public Employee(Integer id,String name) {
this.id = id;
this.name = name;
}
}
**对象::非静态方法
:接口抽象方法a在被重写时使用了某一个对象的方法b,如果方法a和b的形参列表,返回值类型都相同,则可以使用方法b实现对方法a的重写替换 **
@Test
public void test() {
// 使用Lambda表达式
Consumer<String> consumer01 = s -> System.out.println(s);
consumer01.accept("她的手只有我的手四分之三那麼大");
System.out.println("-----------------------------");
// 使用方法引用
//PrintStream printStream = System.out;
//Consumer consumer02 = printStream::println;
Consumer<String> consumer02 = System.out::println;
consumer02.accept("可我還是沒能抓住");
}
@Test
public void test() {
Employee emp = new Employee(1001,"Tom");
// 使用Lambda表达式
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
System.out.println("*******************");
// 使用方法引用
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
类::静态方法
: :接口抽象方法a在被重写时使用了某一个类的静态方法b,如果方法a和b的形参列表,返回值类型都相同,则可以使用方法b实现对方法a的重写替换
@Test
public void test07() {
// 使用Lambda表达式
Comparator<Integer> comparator01 = (o1, o2) -> Integer.compare(o1, o2);
System.out.println(comparator01.compare(20, 77));
System.out.println("----------------------------");
// 使用方法引用
Comparator<Integer> comparator02 = Integer::compare;
System.out.println(comparator02.compare(94, 21));
}
@Test
public void test08(){
// 使用Lambda表达式
Function<Double,Long> function01 = aDouble -> Math.round(aDouble);
System.out.println(function01.apply(3.141));
System.out.println("------------------------------");
// 使用方法引用
Function<Double,Long> function02 = Math::round;
System.out.println(function02.apply(2.717));
}
类::实例方法
: 抽象方法a在被重写时使用了某一个对象的方法b,如果方法a和b的返回值类型相同但方法b的形参少一个,则可以使用方法b实现对方法a的重写替换
@Test
public void test5() {
// 使用Lambda表达式
Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc","abd"));
System.out.println("*******************");
// 使用方法引用
Comparator<String> com2 = String :: compareTo;
System.out.println(com2.compare("abd","abm"));
}
@Test
public void test10(){
// 使用Lambda表达式
BiPredicate<String,String> biPredicate01 = (o1, o2) -> o1.equals(o2);
System.out.println(biPredicate01.test("Kyle", "Kyle"));// true
System.out.println("----------------------------------");
// 使用方法引用
BiPredicate<String,String> biPredicate02 = String::equals;
System.out.println(biPredicate02.test("Violet", "Violet"));// true
}
@Test
public void test7() {
Employee employee = new Employee(1001, "Jerry");
// 使用Lambda表达式
Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(employee));
System.out.println("*******************");
// 使用方法引用
Function<Employee,String> func2 = Employee::getName;
System.out.println(func2.apply(employee));
}
@Test
public void test11(){
Ememployee employee = new Employee(1001, "Jerry");
Function<Stu,String> function01 = employee -> employee.toString();
System.out.println(function01.apply(employee));
System.out.println("------------------------------");
Function<Employee,String> function02 = Employee::toString;
System.out.println(function02.apply(employee employee));
}
类名::new
: 要求Lambda体只有一句语句并且是用来创建一个对象,构造方法的形参列表和返回值(构造器对应类的对象)要与接口中抽象方法一致才可以替换
@Test
public void test1(){
// 传统写法
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println("*******************");
// Lambda表达式
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
System.out.println("*******************");
// 构造器引用
Supplier<Employee> sup2 = Employee :: new;
System.out.println(sup2.get());
}
@Test
public void test2(){
// Lambda表达式
Function<Integer,Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
System.out.println(employee);
System.out.println("*******************");
// 构造器引用
Function<Integer,Employee> func2 = Employee :: new;
Employee employee1 = func2.apply(1002);
System.out.println(employee1);
}
@Test
public void test3(){
// Lambda表达式
BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
System.out.println(func1.apply(1001,"Tom"));
System.out.println("*******************");
// 构造器引用
BiFunction<Integer,String,Employee> func2 = Employee :: new;
System.out.println(func2.apply(1002,"Tom"));
}
数组类型名[]::new
: 要求Lambda体只有一条语句并且是创建一个数组对象,接口中抽象方法的形参接收的是数组对象的长度
@Test
public void test4(){
// Lambda表达式
Function<Integer,String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));
System.out.println("*******************");
// 数组引用
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
}