1 函数式接口java.util.function
https://www.cnblogs.com/CobwebSong/p/9593313.html
2 JAVA8的java.util.function包
https://www.cnblogs.com/linzhanfly/p/9686941.html
一 概述
name | type | description |
---|---|---|
Consumer | Consumer< T > | 接收T对象,不返回值 |
Predicate | Predicate< T > | 接收T对象并返回boolean |
Function | Function< T, R > | 接收T对象,返回R对象 |
Supplier | Supplier< T > | 提供T对象(例如工厂),不接收值 |
UnaryOperator | UnaryOperator< T > | 接收T对象,返回T对象 |
BiConsumer | BiConsumer<T, U> | 接收T对象和U对象,不返回值 |
BiPredicate | BiPredicate<T, U> | 接收T对象和U对象,返回boolean |
BiFunction | BiFunction<T, U, R> | 接收T对象和U对象,返回R对象 |
BinaryOperator | BinaryOperator< T > | 接收两个T对象,返回T对象 |
参考:https://blog.csdn.net/huo065000/article/details/78964382
二 Consumer
1 作用
- 消费某个对象
2 使用场景
- Iterable接口的forEach方法需要传入Consumer,大部分集合类都实现了该接口,用于返回Iterator对象进行迭代。
3 设计思想
- 开发者调用ArrayList.forEach时,一般希望自定义遍历的消费逻辑,比如:输出日志或者运算处理等。
- 处理逻辑留给使用者,使用灵活多变。
- 多变的逻辑能够封装成一个类(实现Consumer接口),将逻辑提取出来。
PASS:Javascript能够将函数传递给另一个函数,这应该算是函数式编程的一个体现,java的function包中的类也是类似的。
public interface Iterable<T> {
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
4 DEMO
public class ConsumerTest {
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
String[] prefix = {"A", "B"};
for (int i = 1; i <= 10; i++)
employees.add(new Employee(prefix[i % 2] + i, i * 1000));
employees.forEach(new SalaryConsumer());
employees.forEach(new NameConsumer());
}
static class Employee {
private String name;
private int salary;
public Employee() {
this.salary = 4000;
}
public Employee(String name, int salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public String toString() {
return new StringBuilder()
.append("name:").append(name)
.append(",salary:").append(salary)
.toString();
}
}
// 输出需要交税的员工
static class SalaryConsumer implements Consumer<Employee> {
@Override
public void accept(Employee employee) {
if (employee.getSalary() > 2000) {
System.out.println(employee.getName() + "要交税了.");
}
}
}
// 输出需要名字前缀是‘A’的员工信息
static class NameConsumer implements Consumer<Employee> {
@Override
public void accept(Employee employee) {
if (employee.getName().startsWith("A")) {
System.out.println(employee.getName() + " salary is " + employee.getSalary());
}
}
}
}
三 Predicate
1 作用
- 判断对象是否符合某个条件
2 使用场景
ArrayList的removeIf(Predicate):删除符合条件的元素
如果条件硬编码在ArrayList中,它将提供无数的实现,但是如果让调用者传入条件,这样ArrayList就可以从复杂和无法猜测的业务中解放出来。
3 设计思想
- 提取条件,让条件从处理逻辑脱离出来,解耦合
4 DEMO
// employee.getSalary() > 2000 提取成一个条件类
class SalaryConsumer implements Consumer<Employee> {
@Override
public void accept(Employee employee) {
// 自行传入本地的最低交税工资
if (new SalaryPredicate(2000).test(employee)) {
System.out.println(employee.getName() + "要交税了.");
}
}
}
class SalaryPredicate implements Predicate<Employee>{
private int tax;
public SalaryPredicate(int tax) {
this.tax = tax;
}
@Override
public boolean test(Employee employee) {
return employee.getSalary() > tax;
}
}
三 Function
1 作用
- 实现一个”一元函数“,即传入一个值经过函数的计算返回另一个值。
2 使用场景
- V HashMap.computeIfAbsent(K , Function<K, V>) // 简化代码,如果指定的键尚未与值关联或与null关联,使用函数返回值替换。
- <R> Stream<R> map(Function<? super T, ? extends R> mapper); // 转换流
3 设计思想
- 一元函数的思想,将转换逻辑提取出来,解耦合
4 DEMO
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
String[] prefix = {"B", "A"};
for (int i = 1; i <= 10; i++)
employees.add(new Employee(prefix[i % 2] + i, i * 1000));
int[] expenses = ListToArray(employees, new EmployeeToExpenses());// 公司对单个员工的支出数组
int[] incomes = ListToArray(employees, new EmployeeToIncome()); // 单个员工的收入数组
System.out.println("社保+公积金+税=" + (sum(expenses) - sum(incomes)) + "元");
}
private static int[] ListToArray(List<Employee> list, Function<Employee, Integer> function) {
int[] ints = new int[list.size()];
for (int i = 0; i < ints.length; i++)
ints[i] = function.apply(list.get(i));
return ints;
}
private static int sum(int[] salarys) {
int sum = 0;
for (int i = 0; i < salarys.length; i++)
sum += salarys[i];
return sum;
}
// 公司支出
static class EmployeeToExpenses implements Function<Employee, Integer> {
@Override
public Integer apply(Employee employee) {
// 假设公司公积金和社保为工资的20%
return Double.valueOf(employee.getSalary() * (1 + 0.2)).intValue();
}
}
// 员工实际到手工资
static class EmployeeToIncome implements Function<Employee, Integer> {
@Override
public Integer apply(Employee employee) {
// 假设员工薪水 * 80% 为到手工资
return Double.valueOf(employee.getSalary() * (1 - 0.2)).intValue();
}
}
四 Supplier
1 作用
- 创建一个对象(工厂类)
2 使用场景
- Optional.orElseGet(Supplier<? extends T>):当this对象为null,就通过传入supplier创建一个T返回。
3 设计思想
- 封装工厂创建对象的逻辑
4 DEMO
public static void main(String[] args) {
// 生成固定工资的员工
Supplier<Employee> supplier = () -> new Employee();
Employee employee1 = supplier.get();
employee1.setName("test1");
Employee employee2 = supplier.get();
employee2.setName("test2");
System.out.println("employee1:" + employee1);
System.out.println("employee2:" + employee2);
}
五 UnaryOperator
1 作用
- UnaryOperator继承了Function,与Function作用相同
- 不过UnaryOperator,限定了传入类型和返回类型必需相同
2 使用场景
- List.replaceAll(UnaryOperator) // 该列表的所有元素替换为运算结算元素
- Stream.iterate(T,UnaryOperator) // 重复对seed调用UnaryOperator来生成元素
3 设计思想
- 一元函数的思想,将同类转换逻辑提取出来,解耦合
4 DEMO
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
String[] prefix = {"B", "A"};
for (int i = 1; i <= 10; i++)
employees.add(new Employee(prefix[i % 2] + i, i * 1000));
System.o
ut.println("公司进行薪资调整...");
salaryAdjustment(employees,new SalaryAdjustment(4000));
employees.forEach(System.out::println);
}
static void salaryAdjustment(List<Employee> list, UnaryOperator<Employee> operator) {
for (int i = 0; i < list.size(); i++) {
list.set(i, operator.apply(list.get(i)));
}
}
static class SalaryAdjustment implements UnaryOperator<Employee> {
private int salary;
public SalaryAdjustment(int salary) {
this.salary = salary;
}
@Override
public Employee apply(Employee employee) {
employee.setSalary(salary);
return employee;
}
}
______________________________________________________________________________________
Java函数式编程
函数式编程
从JDK1.8开始为了简化使用者进行代码的开发,专门提供有lambda表达式的支持,利用此操作形式可以实现函数式的编程,对于函数编程比较著名的语言是:haskell、Scala,利用函数式的编程可以避免掉面向对象编程过程中的一些繁琐的问题。
面向对象在其长期发展的过程中一直有一部分的反对者,这些反对者认为面向对象的设计过于复杂繁琐,以一个最简单的程序为例:
范例:观察传统开发中的问题
1 interface IMessage{
2 public void send(String str);
3 }
4 public class Main {
5
6 public static void main(String[] args) {
7 IMessage msg=new IMessage() {
8 @Override
9 public void send(String str) {
10 System.out.println("消息发送:"+str);
11 }
12 };
13 msg.send("hello");
14 }
15 }
1,Lamda表达式在这个程序中,实际上核心的功能只有一行语句【System.out.println("消息发送:"+str);】,但是为了这样一行核心语句我们需要按照完整的面向对象给出的设计结构进行开发。于是这些问题随着技术的不断发展也是越来越突出。
范例:使用Lambda表达式实现上面相同的功能
1 interface IMessage{
2 public void send(String str);
3 }
4 public class Main {
5
6 public static void main(String[] args) {
7 IMessage msg=(str)->{
8 System.out.println("消息发送:"+str);};
9 msg.send("hello");
10 }
11 }
Lambda表达式如果想要使用,那么必须有一个重要的实现要求:SAM(Single Abstract Method),只有一个抽象方法,在这个接口里面只是提供一个send()方法,除此之外没有任何其他方法定义,所以这样的接口就被定义为函数式接口,而只有函数式接口才能被Lambda表达式所使用。现在整个程序代码里面会发现真的只是编写了一行语句,于是利用这种形式就避免了复杂的面向对象结构化的要求。
范例:函数式接口里面的方法只能有一个
1 @FunctionalInterface//函数式接口
2 interface IMessage{//JDK1.8之后接口里面可以定义普通方法和Static方法
3 public void send(String str);
4 public default void print() {//这是一个
5 System.out.println("公共方法");
6 }
7 }
8 public class Main {
9
10 public static void main(String[] args) {
11 IMessage msg=(str)->{
12 System.out.println("消息发送:"+str);};
13 msg.send("hello");
14 msg.print();
15 }
16 }
①方法没有参数:()->{}对于Lambda表达式而言,需要提供有如下几种格式:
②方法有参数:(参数,参数)->{}
③如果现在只有一行语句返回:(参数,参数)->语句;
范例:定义没有参数的方法
1 @FunctionalInterface//函数式接口
2 interface IMessage{//JDK1.8之后接口里面可以定义普通方法和Static方法
3 public void send();
4 }
5 public class Main {
6 public static void main(String[] args) {
7 IMessage msg=()->{
8 System.out.println("消息发送:这是个无参lambda形式");};
9 msg.send();
10 }
11 }
范例:定义有参数的
1 @FunctionalInterface//函数式接口
2 interface IMath{//JDK1.8之后接口里面可以定义普通方法和Static方法
3 public int add(int x,int y);
4 }
5 public class Main {
6 public static void main(String[] args) {
7 IMath math=(t1,t2)->{
8 return t1+t2;
9 };
10 System.out.println(math.add(10,20));
11 }
12 }
范例:简化Lambda操作以上表达式只有一行语句,这个时候也可以进一步简化。
1 @FunctionalInterface//函数式接口
2 interface IMath{//JDK1.8之后接口里面可以定义普通方法和Static方法
3 public int add(int x,int y);
4 }
5 public class Main {
6 public static void main(String[] args) {
7 IMath math=(t1,t2)->t1+t2;{
8 };
9 System.out.println(math.add(10,20));
10 }
11 }
利用lambda可以摆脱传统面向对象的关于结构的限制,使得代码更加的简便。只用于替换SAM的函数式接口
2,方法引用
引用数据类型最大的特点是可以进行内存的指向处理,但是在我们传统的开发之中一直所使用的只是对象的引用操作,从JDK1.8之后也有方法的引用,即不同的方法名称可以描述同一个方法。如果进行方法的引用在java里面提供有如下的四种形式。
①引用静态方法: 类名称::static 方法名称;
②引用某个实例对象的方法: 实例化对象::普通方法;
③引用特定类型的方法: 特定类::普通方法;
④引用构造方法: 类名称::new。
在String类里面提供有String.valueOf()方法,这个方法属于静态方法。
方法定义:public static String valueOf(int i),该方法有参数,并且有返回值;
·范例:
1 @FunctionalInterface//函数式接口
2 interface IFunction<P,R>{//P描述参数,R描述返回值
3 public R change(P p);
4 }
5 public class Main {
6 public static void main(String[] args) {
7 IFunction<Integer,String>fun=String::valueOf;
8 String str=fun.change(100);
9 System.out.println(str.length());
10 }
11 }
·范例:引用实例化对象中的方法利用方法引用这一概念可以为一个方法定义多个名字,但是要求必须是函数式接口。
在String类里面有一个转大写的方法:public String toUpperCase();
·这个方法是必须有实例化对象提供的情况下才可以调用;
1 @FunctionalInterface//函数式接口
2 interface IFunction<R>{//P描述参数,R描述返回值
3 public R upper();
4 }
5 public class Main {
6 public static void main(String[] args) {
7 IFunction<String>fun="hello"::toUpperCase;
8 String str=fun.upper();
9 System.out.println(str);
10 }
11 }
这是一个普通方法,如果要引用普通方法,则往往都需要实例化对象,但是如果说现在你不想给出实例化对象,只是想引用这个方法。则就可以使用特定类进行引用处理。在进行方法引用的时候也可以进行特定类的一些操作方法,在String类中提供一个字符串大写关系的比较: public int compareTo(String anotherString);
·范例:引用特定类中的方法
1 @FunctionalInterface//函数式接口
2 interface IFunction<P>{//P描述参数,R描述返回值
3 public int compare(P p1,P p2);
4 }
5 public class Main {
6 public static void main(String[] args) {
7 IFunction<String>fun=String::compareTo;
8 System.out.println(fun.compare("A","a"));
9 }
10 }
范例:引用构造方法在方法里面最具杀伤力的是构造方法的引用。
1 class Person{
2 private String name;
3 private int age;
4 public Person(String name,int age){
5 this.name=name;
6 this.age=age;
7 }
8 @Override
9 public String toString() {
10 return "姓名:"+this.name+",年龄:"+this.age;
11 }
12 }
13 @FunctionalInterface//函数式接口
14 interface IFunction<R>{//P描述参数,R描述返回值
15 public R create(String s,int a);
16 }
17 public class Main {
18 public static void main(String[] args) {
19 IFunction<Person>fun=Person::new;
20 System.out.println(fun.create("Wanyu",25));
21 }
22 }
提供方法引用的概念更多的情况下也只是弥补了对于引用的支持功能。
3,内建函数式接口
在JDK1.8之中提供Lambda表达式也提供方法引用,但是你会发现现在由开发者自己定义函数式接口往往需要使用【@FunctionInterface】注解来进行大量的声明,于是很多的情况下如果为了方便则可以引用系统中提供的函数式接口。
在系统值周专门提供有有一个java.util.function的开发包,里面可以直接使用函数式接口,在这个包下面可以直接使用如下的几个核心接口:
①功能性函数式接口:
·在String类中有一个方法判断是否以指定的字符串开头:public boolean startsWith(String str)
接口定义 |
接口使用 |
@FunctionalInterface public interface Function<T,R>{ public R apply(T t); } |
import java.util.function.*; |
②消费型函数式接口:只能进行数据处理操作
·在进行系统数据输出的时候使用的是:System.out.println()
接口定义 |
接口使用 |
@FunctionalInterface public interface Consumer<T>{ public void accept(T t); } |
import java.util.function.*; |
③供给型函数式接口:
·在String类中提供有转小写方法,这个方法没有接收参数,但是有返回值;
|-方法:public String toLowerCase();
接口定义 |
接口使用 |
@FunctionalInterface public interface Supplier<T>{ public T get(); } |
import java.util.function.*; |
④断言型函数式接口:进行判断处理
·在String类有一个compareToIgnoreCase()
接口定义 |
接口使用 |
@FunctionalInterface public interface Predicate<T>{ public boolean test(T t); } |
import java.util.function.*; |
以后对于实际开发之中,如果JDK本身提供的函数式接口可以被我们所使用,那么就没有必要重新定义了。