这几个接口都是在java.util.function包下
Consumer
这是一个消费型的接口,通过传入参数,然后输出值.
/**
* Consumer实例
* 该接口有一个accept方法需要传递一个参数,还有一个默认方法
*/
@Test
public void test03(){
Consumer<String> stringConsumer = new Consumer<String>(){
/**
* Performs this operation on the given argument.
*
* @param s the input argument
*/
@Override
public void accept(String s) {
// 这里可以对传递进来的参数 进行操作
// s="我是改写的参数";
System.out.println(s);
}
};
stringConsumer.accept("我是传递的参数");
System.out.println("==============================");
// Stream流的forEach使用的就是 Consumer类型的参数 Consumer<? super T> action
Stream<String> stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
// 这就意味着我们可以使用 Consumer接口来对于stream流的数据进行过滤或者操作
// stream.forEach(stringConsumer);
// 例子 有个要点,就是stream流只能被消费一次,即每次创建的steam流只能被用一次,所以上面的那个需要注释掉或者再次创建一个steam流
System.out.println("==============================");
stream.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
// 对传递的字符串进行操作
if(s.equals("aaa")){
s="我是aaa";
}
System.out.println(s);
}
});
System.out.println("==============================");
Stream<String> stream02= Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
// stream02.forEach(System.out::print);
// 使用lambda表达式输出,这就是我们常用的那种,本质就是传递一个实现Consumer接口,重写其中的accept方法
// Consumer<String> consumer1 = (s) -> { System.out.println(s); };
stream02.forEach(s -> {
System.out.println(s);
});
}
小结
● Consumer是一个接口,其主要方法就是重写accept方法,就可以输出信息或者对信息进行操作
● lambda表达式就是隐式的实现接口来去作为Stream.forEach的参数 实现遍历.
Supplier
这个接口是一个供给型的接口,可以用来存储数据,
/**
* Supplier接口实例,相当于一个容器存储值
*/
@Test
public void test04() throws Throwable {
// Supplier接口只有一个get方法 没有参数,
Supplier<Double> supplier = Math::random;
Object o = supplier.get();
System.out.println(o);
// 为什么要说他是一个容器呢?
Supplier<String> str = new Supplier<String>() {
@Override
public String get() {
return "我是一个容器";
}
};
String s = str.get();
System.out.println(s);
// 看上去 的确像个容器 可以返回任意类型的对象 和集合和map的确有些类似
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
// 获得一个Optional对象 发现Optianal对象也有一个get方法
Optional<Integer> first = stream.filter(a -> a > 3).findFirst();
if(first.isPresent()){ // isPresent方法是判断该对象是否为空
Integer integer = first.get();
System.out.println(integer);
}
System.out.println("==============================================");
// 但是 Optional对象并没有实现Supplier接口,他的有一些方法需要 Supplier 类型的参数
// orElseGet()方法 如果Supplier类型的参数返回的与 Optional对象(值为4)不一致就返回Optional对象中的数值,
// 如果 Optional对象是null就返回Supplier类型的参数返回的值(值为 1) 直接点开orElseGet()方法也能清晰的看到该方法写了一个三元运算式
Integer integer = first.orElseGet(new Supplier<Integer>() {
@Override
public Integer get() {
return 1;
}
});
// 下面那个方法 是 如果first 为null 就将参数 other 设置进去,否则就返回 first 的值
Integer integer1 = first.orElse(2);
System.out.println(integer);
System.out.println(integer1);
// 这个方法就是如果 first2 为 null就抛出异常 我们这里就把first2做成null
Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5, 6);
Optional<Integer> first2 = stream2.filter(a -> a > 7).findFirst();
first2.orElseThrow(new Supplier<Throwable>() {
@Override
public Throwable get() {
return new Exception("我是一个异常哦");
}
});
}
小结
● Supplier 可以理解为一个只能装一个对象的容器,可以跟optional类型的对象配合
● 该接口只有一个get()方法比较简单,还有一些跟这个类似的接口,使用方法一样
● IntSupplier 、DoubleSupplier 、LongSupplier 、BooleanSupplier
疑问
为什么不直接使用值,非得创建一个接口做一个取值操作?感觉有点多此一举
或许这就是面向接口编程,可以将任意参数都转换为该类型的接口,去其他的类的方法进行统一入参,
面向接口编程定义: 面向接口编程(Interface Oriented Programming:OIP)是一种编程思想,接口作
为实体抽象出来的一种表现形式,用于抽离内部实现进行外部沟通,最终实现内部变动而不影响外部与其他实现交互,可以理解成按照这种思想来设计编程的方式就可以称为面向接口编程。
不过我也看到一个比较有意思的回答算是回答了这个问题,下面是地址,可以去看一下
链接: 在Java中使用Supplier的优势是什么?
我自己也对这个问题进行了思考,写了一个小demo
package top.oneyi;
import java.util.function.Supplier;
/**
* 汽车父类
*/
public class Vehicle {
private String name;
private Integer age;
private boolean flag;
public void driver() {
System.out.println("我在开" + this.name + "兜风");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
// 奔驰类
class BenChi extends Vehicle {
public BenChi() {
}
public BenChi(String name) {
this.setName(name);
}
public BenChi(String name,Integer age) {
this.setName(name);
this.setAge(age);
}
// 复写父类方法
public void driver() {
System.out.println("尊贵的奔驰车主,欢迎使用我们的" + super.getName() + "兜风");
}
public void isfix(Integer age){
if(age > 6){
System.out.println(this.getName() +"需要修理了");
}else{
System.out.println(this.getName() +"不需要修理");
}
}
}
// 宝马类
class BaoMa extends Vehicle {
public BaoMa() {
}
public BaoMa(String name,Integer age) {
this.setName(name);
this.setAge(age);
}
public BaoMa(String name) {
this.setName(name);
}
// 复写父类方法
public void driver() {
System.out.println("尊贵的宝马车主,欢迎使用我们的" + super.getName() + "兜风");
}
public void see(){
System.out.println("观看" + super.getName());
}
public void isfix(Integer age){
if(age > 5){
System.out.println("该车需要修理了");
}
}
}
class demo {
/**
* 使用Supplier 作为参数
* @param car
* @return
*/
public static Vehicle diver(Supplier<? extends Vehicle> car) {
return car.get();
}
/**
* 使用父类作为参数
* @param car
* @return
*/
public static void diver02(Vehicle car) {
car.driver();
}
/**
* 中间层需要 新加车辆是否需要修理方法
* @param car
*/
public static void diver03(Vehicle car) {
BenChi car1 = (BenChi) car;
car1.isfix(car1.getAge());
}
}
class test{
public static void main(String[] args) {
demo.diver(new Supplier<Vehicle>() {
@Override
public Vehicle get() {
return new BenChi("奔驰600");
}
}).driver();
demo.diver(()->{return new BenChi("奔驰600");}).driver();
demo.diver02(new BenChi("奔驰300"));
// 使用Supplier接口可以使用lambda表达式,让代码看起来更加简洁
System.out.println("==============================");
demo.diver(()->{
// 使用Supplier接口可以重写get方法来对Vehicle对象进行属性变动
BenChi benChi = new BenChi("奔驰600");
if(benChi.getName().equals("奔驰600")){
benChi.setName("奔驰300");
}
return benChi;
}).driver();
System.out.println("==============================");
// 看下面一个例子
// 将设我们的车现在加了一个新的属性age,
demo.diver(()->{
// 使用Supplier接口可以重写get方法来对Vehicle对象进行属性变动
BenChi benChi = new BenChi("奔驰600");
benChi.isfix(7);
return benChi;
});
// 这时候发现,BenChi,自己特有的方法父类不能实现只能进行强制转换,可以在之前的方法进行修改 或者直接新写一个方法
demo.diver03(new BenChi("奔驰300",7));
// 弊端出现了,如果Vehicle新加属性,并且子类对于该属性进行方法扩展,使用父类来当作参数的类的方法就需要重写,或者新加方法, 这样相当源代码就需要更改,风险很大,
// 使用 Supplier 接口则是自己使用lambda表达式自己改变对应的方法,好处是中间层 demo不需要做成改变,把这个过程抽象成三层,第一层为 父类和子类 第二层是供应商类 第三层为客户类调用方
// 当第一层做出改变,使用接口则第二层供应商类无需做出改变,第三层自己根据第一层的改变 自己改变自己的调用方式 或许这就是 Supplier 叫供给型接口的原因
}
}
经过上面的demo,我也能理解上面那个链接最后解释的例子.第一层是属于汽车制造业,第二层是4S店,第三层是车主就像最后加的那个age属性,4s店肯定不能随时知道你的车辆情况,他也知道不了,他只知道卖车,还有就是打电话让你保养.只能用户是否知道自己的车是否需要修理(是否爆胎啊,撞到花池啥的).
Predicate
Predicate 接口是一个谓词型接口,返回值为布尔类型
这个比较简单就是返回布尔类型的
/**
* Predicate接口 只有一个test方法需要重写 其他都是默认方法
*/
@Test
public void test05(){
Predicate<Integer> predicate = new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer > 5;
}
};
boolean test = predicate.test(8);
System.out.println(test);
System.out.println("=============================");
// lambda表达式
predicate = (t) -> t > 5;
System.out.println(predicate.test(3));
System.out.println("=============================");
// Stream流中的filter参数类型就是 Predicate 大于 5的都会输出
Stream<Integer> stream = Stream.of(1, 23, 3, 4, 5, 56, 6, 6);
stream.filter(predicate).forEach(System.out::println);
}
小结
该接口就是起一个判断的作用,就没有什么其他作用了
Function
Function接口是一个功能型的接口,它的一个作用就是转换作用,将输入数据转换成另一种形式的输出数据.
/**
* Function 接口是一个功能型接口,
* 它的一个作用就是转换作用,将输入
* 数据转换成另一种形式的输出数据。
*/
@Test
public void test06(){
//泛型的第一个参数是转换前的类型,第二个是转化后的类型
Function<String,Integer> fn = new Function<String,Integer>(){
@Override
public Integer apply(String s) {
return s.length();
}
};
// 使用Stream流的map方法将字符串转换为字符串的长度
Stream<String> stream = Stream.of("aaa", "bbbbb", "ccccccv");
stream.map(fn).forEach(System.out::println);
System.out.println("=============================");
Stream<String> stream2= Stream.of("aaa", "bbbbb", "ccccccv");
List<Integer> list = stream2.map(s -> {
if(s.length() > 4){
return s.length();
}else{
return 0;
}
}).collect(Collectors.toList());
list.forEach(System.out::println);
}
Stream流的map方法就是用的这个类型的参数,其实也都是过滤数据用的,除了这个接口还有其他的类似的接口
IntFunction 、DoubleFunction 、LongFunction 、ToIntFunction 、ToDoubleFunction 、DoubleToIntFunction 等等,使用方法和上面一样。
这四个接口大概就讲完了,使用倒是不难,就是很抽象,大家可以去看原文章,写的也很好.