函数式接口介绍
如果在一个接口中只声明了一个抽象方法,则此接口就被称为函数式接口
(该接口可以包含其他非抽象方法)
- 接口上使用
@FunctionalInterface注解
可以验证该接口是否为函数式接口,javadoc
生成的文档时也会保留该注解, 若接口中有多个抽象方法编译器会报错
随着Python,Scala
等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,所以Java8不但可以支持OOP还可以支持OOF(面向函数编程)
面向对象编程思想
:完成一件事情需要找一个能解决这个事情的对象然后调用对象的方法函数式编程思想
: 重视结果不重视过程,只要能获取到结果即可,无论谁去做又怎么做- 在函数式编程语言当中Lambda表达式的类型是函数,但在Java8中Lambda表达式是对象而不是函数,它们必须依附于函数式接口
Java内置函数式接口
在java.util.function
包下定义了Java8的丰富的函数式接口
四大核心函数式接口
函数式接口 | 称谓 | 参数类型 | 用途 |
---|---|---|---|
Consumer<T> | 消费型接口 | T | 对类型为T的对象应用操作,包含方法 void accept(T t) |
Supplier<T> | 供给型接口 | 无 | 返回类型为T的对象,包含方法T get() |
Function<T, R> | 函数型接口 | T | 对类型为T的对象应用操作并返回结果R类型的对象,包含方法R apply(T t) |
Predicate<T> | 判断型接口 | T | 确定类型为T的对象是否满足某约束并返回 boolean 值,包含方法boolean test(T t) |
消费型接口
: 抽象方法有形参但是返回值类型是void
接口名 | 抽象方法 | 描述 |
---|---|---|
BiConsumer<T,U> | 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<T,U,R> | R apply(T t, U u) | 接收一个T类型和一个U类型对象,返回一个R类型对象结果 |
BinaryOperator | T apply(T t, T u) | 接收两个T类型对象,返回一个T类型对象结果 |
ToDoubleBiFunction<T,U> | double applyAsDouble(T t, U u) | 接收一个T类型和一个U类型对象,返回一个double |
ToIntBiFunction<T,U> | int applyAsInt(T t, U u) | 接收一个T类型和一个U类型对象,返回一个int |
ToLongBiFunction<T,U> | 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<T,U> | 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 interface Consumer<Double>{
void accept (Double money);
}
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);
}
Lambda表达式语法的使用
匿名内部类
当需要启动一个线程去完成任务时,通常会通过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表达式的6种形式
我们可以通过Lambda表达式创建函数式接口的实现类对象代替匿名实现类
,Lambda表达式是用来简化函数式接口的变量或形参赋值的语法
- 因为
函数式接口只有一个抽象方法
,所以我们才可以省略方法名称,方法参数,方法返回值,@Override函数声明
等内容
new Thread(() -> {
System.out.println("多线程任务执行!");
}).start();
Lambda表达式的语法格式如(o1,o2) -> Integer.compare(o1,o2)
->
:lambda操作符或箭头操作符->左边(函数式接口中抽象方法的形参列表)
:因为有类型推断机制
形参的数据类型都可以省略,如果只有一个参数,参数的小括号可以省略->右边{函数式接口中抽象方法的方法体}
: 当Lambda体只有一条语句时(return语句或其他语句),如果是return语句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与{}
需要一起省略, 注意不能只省略{}
即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));
}