函数式编程(lambda表达式 @FunctionalInterface)
思想:(函数式编程VS面向对象)
函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。编程中的函数,也有类似的概念,你调用我的时候,给我实参为形参赋值,然后通过运行方法体,给你返回一个结果。对于调用者来做,关注这个方法具备什么样的功能。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。
面向对象的思想:
做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情.
函数式编程思想:
只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程
优点:
目的是简化匿名内部类
Lambda写的好可以极大的减少代码冗余,同时可读性也好过冗长的匿名内部类。
冗余的匿名内部类
public class Demo01Runnable {
public static void main(String[] args) {
// 匿名内部类
Runnable task = new Runnable() {
@Override
public void run() { // 覆盖重写抽象方法
System.out.println("多线程任务执行!");
}
};
new Thread(task).start(); // 启动线程
}
}
分析弊端:
对于
Runnable
的匿名内部类用法,可以分析出几点内容:
Thread
类需要Runnable
接口作为参数,其中的抽象run
方法是用来指定线程任务内容的核心;为了指定
run
的方法体,不得不需要Runnable
接口的实现类;为了省去定义一个
RunnableImpl
实现类的麻烦,不得不使用匿名内部类;必须覆盖重写抽象
run
方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;而实际上,似乎只有方法体才是关键所在。
编程思想转换
做什么,而不是谁来做,怎么做 (关注点不同)
我们真的希望创建一个匿名内部类对象吗?不。我们只是为了做这件事情而不得不创建一个对象。我们真正希望做的事情是:将
run
方法体内的代码传递给Thread
类知晓。传递一段代码——这才是我们真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。那,有没有更加简单的办法?如果我们将关注点从“怎么做”回归到“做什么”的本质上,就会发现只要能够更好地达到目的,过程与形式其实并不重要。
函数式编程面向的主要场景是,针对于函数式接口(@FunctionalInterface,即接口中只含有一个抽象方法,对于静态方法和default方法没有限制).
了解函数式接口:
写法:
针对于抽象方法,省略方法声明,只要参数,和方法体,如果方法体只有一个语句也可以省略{},否则可以省略。
()->{}
():形参
->: 语法标识
{}:方法体注意:
1.如果方法重写后 仅有一行代码 可以省略{}
2.()数据类型可以省略
3.如果带返回值的方法 只有一行返回语句
那么可以省略 return {};
(形参列表)它就是你要赋值的函数式接口的抽象方法的(形参列表),照抄
{Lambda体}就是实现这个抽象方法的方法体
->称为Lambda操作符(减号和大于号中间不能有空格,而且必须是英文状态下半角输入方式)
优化:Lambda表达式可以精简
当{Lambda体}中只有一句语句时,可以省略{}和{;}
当{Lambda体}中只有一句语句时,并且这个语句还是一个return语句,那么return也可以省略,但是如果{;}没有省略的话,return是不能省略的
(形参列表)的类型可以省略
当(形参列表)的形参个数只有一个,那么可以把数据类型和()一起省略,但是形参名不能省略
当(形参列表)是空参时,()不能省略
lambda表达式其实就是实现SAM接口的语法糖,所谓SAM接口就是Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法。为了简化匿名内部类的写的方式
其实只要满足“SAM”特征的接口都可以称为函数式接口,都可以使用Lambda表达式,但是如果要更明确一点,最好在声明接口时,加上@FunctionalInterface。一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。
之前学过的SAM接口中,标记了@FunctionalInterface的函数式接口的有:Runnable,Comparator 。
Java8在java.util.function新增了很多函数式接口:主要分为四大类,消费型、供给型、判断型、功能型。基本可以满足我们的开发需求。当然你也可以定义自己的函数式接口。
函数式编程只关注结果
函数式接口的分类:
(一)自定义函数式接口
系统类的函数式接口:
@FunctionalInterface
public interface Runnable {}
@FunctionalInterface
public interface Comparator<T> {}public interface Comparable<T> {}只有一个抽象方法
系统函数式接口的分类:
消费型 貔貅 只进不出 只接受数据 不返回数据
供给型接口 雷锋 只奉献不索取 只返回数据 不接受数据
判断/断言型接口 法官 判断对错 无论接受什么数据 都只返回 boolean功能型接口 普通人 有来有往 既接受数据 也返回数据
只要确保接口中有且仅有一个抽象方法即可。
public class AutoDefination {
public static void main(String[] args) {
A a = new A() {
@Override
public void add(int a, int b) {
System.out.println(a + b);
}
};
a.add(100, 200);
}
@Test
public void test02(){
//lambda表达式只对抽象接口起作用
testAdd(100, 200, (a,b) -> System.out.println(a + b));
}
@Test
public void test01(){
testAdd(100, 200, new A() {
@Override
public void add(int a, int b) {
System.out.println(a + b);
}
});
}
public static void testAdd(int a,int b,A c){
c.add(a, b);
}
}
@FunctionalInterface
interface A{
//创建抽象方法
void add(int a,int b);
}
(二)消费型接口
消费型接口的抽象方法特点:有形参,但是返回值类型是void
举例(list遍历和map遍历lambda)
list遍历
public class ConsumerTest {
ArrayList<String> list = new ArrayList<>();
@Before
public void test00(){
Collections.addAll(list, "A","B","C","D","E");
}
@Test
public void test06(){
}
@Test
public void test05(){
list.forEach((s) -> System.out.println(s));
;
}
@Test
public void test03(){
//会从传入参数的前一个位置开始遍历
ListIterator<String> listIterator = list.listIterator(list.size());
while (listIterator.hasPrevious()){
String current = listIterator.previous();
System.out.println("current = " + current);
}
}
@Test
public void test02(){
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println("next = " + next);
}
}
@Test
public void test01(){
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
map遍历
public class TestMap {
Map<Integer, String> map = new HashMap<>();
@Before
public void test00(){
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");
map.put(4, "D");
map.put(5, "E");
}
@Test
public void test03(){
map.forEach(new BiConsumer<Integer, String>() {
@Override
public void accept(Integer integer, String s) {
System.out.println(integer + "--->" + s);
}
});
}
@Test
public void test02(){
map.forEach((k , v) -> System.out.println(k + "---->" + v));
}
@Test
public void test01(){
Set<Integer> keys = map.keySet();
keys.forEach((key) -> System.out.println(key +"---->" + map.get(key)));
}
}
(三)供给型接口
这类接口的抽象方法特点:无参,但是有返回值
public class SupplierTest {
@Test
public void test03(){
// //生成数据 将数据进行展示 无限生成
/*Stream.generate(new Supplier<Object>() {
@Override
public Object get() {
return new Date();
}
}).forEach(new Consumer<Object>() {
@Override
public void accept(Object o) {
System.out.println("o = " + o);
}
});*/
Stream.generate(() -> new Date()).forEach(System.out::println);
}
@Test
public void test02(){
/*Supplier<Double> supplier = new Supplier<Double>() {
@Override
public Double get() {
return Double.valueOf(Math.PI);
}
};*/
Supplier<Double> supplier = () -> Double.valueOf(Math.PI);
Double aDouble = supplier.get();
System.out.println("aDouble = " + aDouble);
}
@Test
public void test01(){
/*//返回一个对象
Supplier supplier = new Supplier() {
@Override
public Object get() {
return new String("供给型接口作用是返回一个对象");
}
};*/
Supplier supplier = () -> new String("供给型接口作用是返回一个对象");
Object o = supplier.get();
System.out.println("o = " + o);
}
}
(四)判断型 (用于过滤数据)
这里接口的抽象方法特点:有参,但是返回值类型是boolean结果。
public class PredicateTest {
ArrayList<Integer> list = new ArrayList<Integer>();
@Before
public void test00(){
Collections.addAll(list, 1,2,3,4,5,6,7,8,9,10);
list.forEach((n)-> System.out.print(n + " "));
System.out.println();
System.out.println("-------------数据添加完毕-----------------");
}
@Test
public void test01(){
//删除 经过检验 返回值为true的值
/*
//方式一
list.removeIf(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
//删除偶数
if (integer % 2 == 0){
return true;
}else {
return false;
}
}
});*/
//方式二
// list.removeIf((integer) -> integer % 2 == 0? true:false);
//方式三
list.removeIf((integer)->integer%2==0);
}
@After
public void test000(){
System.out.println("------------数据处理完打印----------------");
list.forEach((n)-> System.out.print(n+ " "));
}
}
(五)功能型
这类接口的抽象方法特点:既有参数又有返回值
public class FunctionTest {
HashMap<Integer, String> map = new HashMap<>();
@Before
public void test00() {
map.put(1, "李白");
map.put(2, "安琪拉");
map.put(3, "白居易");
map.put(4, "李商隐");
map.put(5, "嬴政");
map.forEach((k, v) -> System.out.println(k + "--->" + v));
}
@Test
public void test04() {
map.replaceAll((k,v)-> v.contains("李")?"妲己":v);
}
@Test
public void test03() {
map.replaceAll((k,v) -> {
return v.contains("李")?"妲己":v;
});
}
@Test
public void test02() {
map.replaceAll((k, v) -> {
if (v.contains("李")) {
return "妲己";
} else {
return v;
}
});
}
@Test
public void test01() {
map.replaceAll(new BiFunction<Integer, String, String>() {
@Override
public String apply(Integer key, String value) {
// System.out.println(key + "------>" + value);
//只要value中有白 ,就要将此value替换成妲己
if (value.contains("白")) {
return "妲己";
}
//返回值会替换原有的value
return value;
}
});
}
@After
public void test000() {
System.out.println("-----------------");
map.forEach((k, v) -> System.out.println(k + "--->" + v));
}
}
判断型接口 练习
public class Employee {
private int id;
private String name;
private char gender;
private int age;
private double salary;
public Employee(int id, String name, char gender, int age, double salary) {
super();
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
this.salary = salary;
}
public Employee() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", salary=" + salary
+ "]";
}
}
public class EmployeeSerice {
ArrayList<Employee> all;
public EmployeeSerice() {
all = new ArrayList<Employee>();
all.add(new Employee(1, "张三", '男', 33, 8000));
all.add(new Employee(2, "翠花", '女', 23, 18000));
all.add(new Employee(3, "无能", '男', 46, 8000));
all.add(new Employee(4, "李四", '女', 23, 9000));
all.add(new Employee(5, "老王", '男', 23, 15000));
all.add(new Employee(6, "大嘴", '男', 23, 11000));
}
ArrayList<Employee> get(Predicate<Employee> p) {
//在EmployeeSerice员工管理类中,声明一个方法:
// ArrayList<Employee> get(Predicate<Employee> p),
// 即将满足p指定的条件的员工,添加到一个新的ArrayList<Employee> 集合中返回。
ArrayList<Employee> emp = new ArrayList<>();
for (Employee employee : all) {
//底层Predicate接口中有个抽象方法 boolean test(T t); 推断型接口返回类型为Boolean
if (p.test(employee)) {
emp.add(employee);
}
}
return emp;
}
}
class EmployeeTest {
public static void main(String[] args) {
EmployeeSerice es = new EmployeeSerice();
/* es.get(new Predicate<Employee>() {
@Override
public boolean test(Employee employee) {
System.out.println(employee);
return true;
}
});*/
//- 所有员工对象
es.get((e)-> true).forEach((s) -> System.out.println(s));
System.out.println("-----------");
// - 所有年龄超过35的员工
es.get((e) -> e.getAge() > 35 ).forEach(System.out::println);
// - 所有薪资高于15000的女员工
System.out.println("-------------------");
es.get((e) -> e.getSalary()>15000).forEach(System.out::println);
// - 所有编号是偶数的员工
es.get((e) -> e.getId() % 2 == 0).forEach(System.out::println);
// - 名字是“张三”的员工
es.get((e) -> e.getName().equals("张三")).forEach(System.out::println);
// - 年龄超过25,薪资低于10000的男员工
es.get((e) -> e.getAge()>25 && e.getSalary()<10000).forEach(System.out::println);
}
}