# 常用的函数式接口
## 回顾
函数接口的概念在之前的文章《Lambda表达式》中就有提到,函数接口配合Lambda表达式的使用在一些情况下可以大大简化java代码的书写。函数接口指的就是接口中只有一个抽象方法, 并且接口使用@FunctionalInterface注解进行修饰。JDK中帮我们定义好了一些常用的函数接口,这些函数接口配合Lambda和后面要讲的stream流进行一些**过滤数据**的操作,用起来非常简洁。常用在“作为参数传递给一个方法”的场景中。下面就来讲常用到的函数接口有哪些。
## Supplier接口
(supplier 供应商)是一个"工厂"类型接口,用来"生产"对应的数据类型,不用传入任何数据。具体使用在后面的stream流中可以体现。
~~~
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
~~~
### 抽象方法
> T get():用来获取一个泛型参数类型指定的数据,即用来提供一个符合要求的数据类型,"生产"一个数据,就跟"供应商"一样。
举例:获取一个数组中的最大值
```java
public class SupplierDemo {
public static int getMaxValue(Supplier<Integer> supplier) { //接口作为参数进行传递
return supplier.get();
}
public static void main(String[] args) {
int[] nums = {2, 9, 19, 1, 20};
int max = getMaxValue(()->{ //使用Lambad表达式
int max = nums[0];
for (int i = 1; i < nums.length; i++) {
max = nums[i] > max ? nums[i] : max;
}
return max;
});
}
}
```
get()方法的目的就是给我们提供一个想要的数据类型。
## Consumer接口
Consumer接口具有和Supplier接口相反的功能,主要是为了消费一个数据。
~~~
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
~~~
### 抽象方法
> void accept(T):对给定的参数执行该方法的内容。
举例:将传递的字符串数据进行逆转
```java
public class ConsumerDemo {
public static void reserveString(String str, Consumer<String> con) {
con.accept(str);
}
public static void main(String[] args) {
String str = "lambda";
reserveString(str, (str)->{
System.out.println(str); //lambda
StringBuffer reStr = new StringBuffer(str);
reStr.reverse().toString(); //将字符串进行逆转
System.out.println(reStr); //adbmal
});
}
}
```
### 默认方法
与Supplier接口不同的是,Consumer接口中实现了一个默认的方法,addThen()
```java
/**
* 默认方法
* 1. 返回一个组合的Consumer对象,并且会按照顺序执行accept方法:即可以拼接多个进行执行
* 2. 如果after方法执行出现异常(或接下去拼接的出现异常了)就会将该异常转发给调用者(例如main)。
* 3. 如果执行this操作出现异常时,不会执行after的操作。
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after); //判断是否为空指针
return (T t) -> {
accept(t);
after.accept(t);
}; //这也是一个Consumer接口accept的lambad实现
}
```
举例:将一个字符串转化为大小写
```java
public class ConsumerDemo {
public static void method(String str, Consumer<String> con1, Consumer<String> con2) {
con1.andThen(con2).accept(str);
}
public static void main(String[] args) {
String str = "Lambda";
method(str, (t)->{
System.out.println(t.toUpperCase()); //LAMBDA
}, (t) -> {
System.out.println(t.toLowerCase()); //lambda
});
}
}
```
可以使用默认方法实现一个责任链模式。
## Predicate接口
(Predicate 谓词,离散数学中的概念),传入一个参数,对参数进行逻辑判断,看参数是否符合条件(在给定的参数上评估这个谓词),返回值为boolean。
```java
@FunctionalInterface
public interface Predicate<T> { }
```
### 抽象接口
> boolean test(T t):判断传入的参数是否符合条件
举例:传入一个字符串列表,打印长度大于4的字符串
```java
public class PredicateDemo {
public static void checkString(String[] strs, Predicate<String> pre) {
for (int i = 0; i < strs.length; i++) {
if (pre.test(strs[i])) {
System.out.println(strs[i]);
}
}
}
public static void main(String[] args) {
String[] strs = {"java", "c", "C++", "python", "javascript", "Typescript"};
checkString(strs, (str)->{
return str.length() > 4;
}); //python, javascript,Typescript
}
}
```
### 默认方法
既然是用来进行逻辑判断的,少不了要用到逻辑运算符,Predicate接口里面就内置了一些默认的逻辑运算方法:
```java
/**
* 1.返回一个通过逻辑与组合的Predicate接口
* 2.other.test抛出异常的话将该异常转发给调用者
* 3.this.test抛出异常的话不执行other.test的内容
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
//逻辑非
default Predicate<T> negate() {
return (t) -> !test(t);
}
//逻辑或
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
```
举例:判断字符串包含a并且长度大于5
```java
public class PredicateDemo {
public static boolean checkString(String str, Predicate<String> pre1, Predicate<String> pre2) {
return pre1.and(pre2).test(str);
}
public static void main(String[] args) {
String str = "lambda";
boolean b = checkString(str, (str1)->{
return str1.length() > 5;
}, (str1) -> {
return str1.contains("a");
});
System.out.println(b); // true
}
}
```
## Function接口
```java
@FunctionalInterface
public interface Function<T, R> {}
```
该接口用来将类型T转化为类型R,T称为前置条件,R称为后置条件
### 抽象方法
> R apply(T t):将给定参数类型T转化为R类型
例如,将String类型数字转化为Integer类型
```java
public class FunctionDemo {
private static void transform(Function<String, Integer> function) {
int num = function.apply("10"); //数字10
System.out.println(num + 20);
}
public static void main(String[] args) {
transform(s ‐> Integer.parseInt(s));
}
}
```
### 默认方法
与Consumer类似的,Function也有一个addThen方法,该方法也是返回一个组合操作后的Function接口,最终转化的数据类型与after转化的数据类型一致。
```java
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after); //判断是否为空指针
return (T t) -> after.apply(apply(t)); //这也是一个Consumer接口apply的lambad实现
}
```
举例:将字符串数字转化为Integer类型后+10再转化为字符串
```java
public class FunctionDemo{
public static void addTen(String str, Function<String, Integer> fun1, Function<Integer, String> fun2) { //注意最后返回的与fun2的类型相同
String s = fun1.andThen(fun2).apply(str);
System.out.println(s); //"20"
}
public static void main(String[] args) {
String s = "10";
addTen(s, (String str1) ->{
return Integer.parseInt(str1) + 10;
}, (Integer integer) -> {
return integer.toString();
});
}
}
```
## 小结
1. 通过函数式接口,我们可以看到lambda表达式的简洁之处。
2. 虽然现阶段我们可能会觉得这么进行操作多此一举,但是这些接口在后面的stream流就有很大的用处。
- 第一章 Java基础
- ThreadLocal
- Java异常体系
- Java集合框架
- List接口及其实现类
- Queue接口及其实现类
- Set接口及其实现类
- Map接口及其实现类
- JDK1.8新特性
- Lambda表达式
- 常用函数式接口
- stream流
- 面试
- 第二章 Java虚拟机
- 第一节、运行时数据区
- 第二节、垃圾回收
- 第三节、类加载机制
- 第四节、类文件与字节码指令
- 第五节、语法糖
- 第六节、运行期优化
- 面试常见问题
- 第三章 并发编程
- 第一节、Java中的线程
- 第二节、Java中的锁
- 第三节、线程池
- 第四节、并发工具类
- AQS
- 第四章 网络编程
- WebSocket协议
- Netty
- Netty入门
- Netty-自定义协议
- 面试题
- IO
- 网络IO模型
- 第五章 操作系统
- IO
- 文件系统的相关概念
- Java几种文件读写方式性能对比
- Socket
- 内存管理
- 进程、线程、协程
- IO模型的演化过程
- 第六章 计算机网络
- 第七章 消息队列
- RabbitMQ
- 第八章 开发框架
- Spring
- Spring事务
- Spring MVC
- Spring Boot
- Mybatis
- Mybatis-Plus
- Shiro
- 第九章 数据库
- Mysql
- Mysql中的索引
- Mysql中的锁
- 面试常见问题
- Mysql中的日志
- InnoDB存储引擎
- 事务
- Redis
- redis的数据类型
- redis数据结构
- Redis主从复制
- 哨兵模式
- 面试题
- Spring Boot整合Lettuce+Redisson实现布隆过滤器
- 集群
- Redis网络IO模型
- 第十章 设计模式
- 设计模式-七大原则
- 设计模式-单例模式
- 设计模式-备忘录模式
- 设计模式-原型模式
- 设计模式-责任链模式
- 设计模式-过滤模式
- 设计模式-观察者模式
- 设计模式-工厂方法模式
- 设计模式-抽象工厂模式
- 设计模式-代理模式
- 第十一章 后端开发常用工具、库
- Docker
- Docker安装Mysql
- 第十二章 中间件
- ZooKeeper