函数式编程
函数式编程指的是使用Lambda函数替代某些匿名内部类,从而让程序代码更简洁,可读性更好
1.Lambda表达式
1.1 定义
- JDK 8开始新增的一种语法形式,它表示函数
- 可以用于替代某些匿名内部类,从而让程序更简洁,可读性更好
Tips:Lambda表达式只能替代函数式接口的匿名内部类
(被重写方法的形参列表)->
{
被重写方法的方法体代码
}
函数式接口:
- 有且仅有一个抽象方法的接口
- 注意:将来见到的大部分函数式接口,上面都可能会有一个@FunctionalInterface的注解,该注解用于约束当前接口必须是函数式接口
public class Lambdademo {
public static void main(String[] args) {
Animal a = new Animal() {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
};
a.eat();
System.out.println("============================");
Swim s1=new Swim() {
@Override
public void swim() {
System.out.println("学生游泳贼快");
}
}
Swim s2=()->{
System.out.println("学生游泳贼快");
}
}
}
abstract class Animal{
abstract void eat();
}
//函数式接口:有且仅有一个抽象方法的接口
@FunctionalInterface//声明函数式接口的注解
interface Swim{
void swim();
}
分析这段代码:
Animal由于只是抽象类不是接口,所以不能使用Lambda表达式替代
而Swim是接口,且只有一个抽象方法所以能被Lambda表达式替代
**为什么这个Lambda能替代?**因为能推断出上下文,Swim s2能推断出是一个接口,后面的方法体代码能代码能推断出只有一个抽象方法,所以能推断出原来的代码如s1。
1.2 实例
1.2.1 用comparator接口的匿名内部函数类实现对数组进行排序
在匿名内部类中使用comparator接口的匿名内部函数类实现对数组进行排序代码中可以看到编译器用灰色标记Comparator提示可以进行Lambda表达式进行简化,进入该方法的声明,该方法有声明函数式接口的注解,说明该匿名内部类可以使用Lambda表达式进行化简
import com.fqw.anonymousclass.Student;
import java.util.Arrays;
import java.util.Comparator;
public class Lambdademo1 {
public static void main(String[] args) {
Student[] students=new Student[6];
students[0]=new Student("张三", 18, 1.75);
students[1]=new Student("李四", 23, 1.80);
students[2]=new Student("王五", 20, 1.65);
students[3]=new Student("赵六", 17, 1.70);
students[4]=new Student("钱七", 22, 1.65);
students[5]=new Student("孙八", 16, 1.75);
//输出排序前的学生数组
for (int i=0;i<students.length;i++){
Student student = students[i];
System.out.println(student.getName()+","+ student.getAge()+","+ student.getHeight());
}
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2)
{
return o1.getAge()-o2.getAge();//升序
}
});
System.out.println("=======================================");
//输出排序后的学生数组
for (int i=0;i<students.length;i++){
Student student = students[i];
System.out.println(student.getName()+","+ student.getAge()+","+ student.getHeight());
}
}
}
简化后的代码
import com.fqw.anonymousclass.Student;
import java.util.Arrays;
import java.util.Comparator;
public class Lambdademo1 {
public static void main(String[] args) {
Student[] students=new Student[6];
students[0]=new Student("张三", 18, 1.75);
students[1]=new Student("李四", 23, 1.80);
students[2]=new Student("王五", 20, 1.65);
students[3]=new Student("赵六", 17, 1.70);
students[4]=new Student("钱七", 22, 1.65);
students[5]=new Student("孙八", 16, 1.75);
//输出排序前的学生数组
for (int i=0;i<students.length;i++){
Student student = students[i];
System.out.println(student.getName()+","+ student.getAge()+","+ student.getHeight());
}
Arrays.sort(students, (Student o1, Student o2)->{
return o1.getAge()-o2.getAge();//升序
});
System.out.println("=======================================");
//输出排序后的学生数组
for (int i=0;i<students.length;i++){
Student student = students[i];
System.out.println(student.getName()+","+ student.getAge()+","+ student.getHeight());
}
}
}
1.2.2 登录窗口登录
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Lambdademo2 {
public static void main(String[] args) {
//需求:创建一个登录窗口,登录窗口中包含一个登录按钮
JFrame win = new JFrame("登录窗口");
win.setSize(300, 200);
win.setLocationRelativeTo(null);//居中显示
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//创建一个面板,把按钮添加到面板中
JPanel panel = new JPanel();
win.add(panel);
//创建一个按钮
JButton button = new JButton("登录");
panel.add(button);
//java要求必须给按钮添加一个点击事件监听器对象,这样就可以监听用户事件,做出反应
button.addActionListener((ActionEvent e) -> {
System.out.println("登录成功");
});
win.setVisible(true);
}
}
1.3 省略规则(进一步简化Lambda表达式的写法)
- 参数类型全部可以省略不写
- 如果只有一个参数,参数类型省略的同时“()”也可以省略,但多个参数不能省略“()”
- 如果Lambda表达式只有一行代码,大括号可以不写,同时要省略分号”;”,如果这行代码是return语句,也必须去掉return
根据简化规则简化后的代码:
import com.fqw.anonymousclass.Student;
import java.util.Arrays;
import java.util.Comparator;
public class Lambdademo1 {
public static void main(String[] args) {
Student[] students=new Student[6];
students[0]=new Student("张三", 18, 1.75);
students[1]=new Student("李四", 23, 1.80);
students[2]=new Student("王五", 20, 1.65);
students[3]=new Student("赵六", 17, 1.70);
students[4]=new Student("钱七", 22, 1.65);
students[5]=new Student("孙八", 16, 1.75);
//输出排序前的学生数组
for (int i=0;i<students.length;i++){
Student student = students[i];
System.out.println(student.getName()+","+ student.getAge()+","+ student.getHeight());
}
Arrays.sort(students, ( o1, o2) -> o1.getAge()-o2.getAge());
System.out.println("=======================================");
//输出排序后的学生数组
for (int i=0;i<students.length;i++){
Student student = students[i];
System.out.println(student.getName()+","+ student.getAge()+","+ student.getHeight());
}
}
}
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Lambdademo2 {
public static void main(String[] args) {
//需求:创建一个登录窗口,登录窗口中包含一个登录按钮
JFrame win = new JFrame("登录窗口");
win.setSize(300, 200);
win.setLocationRelativeTo(null);//居中显示
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//创建一个面板,把按钮添加到面板中
JPanel panel = new JPanel();
win.add(panel);
//创建一个按钮
JButton button = new JButton("登录");
panel.add(button);
//java要求必须给按钮添加一个点击事件监听器对象,这样就可以监听用户事件,做出反应
button.addActionListener(e -> System.out.println("登录成功"));
win.setVisible(true);
}
}
2.方法引用(了解)
2.1 静态方法引用
类名::静态方法
使用场景:如果某个Lambda表达式里只调用一个静态方法,并且“->”前后参数的形式一致,就可以使用静态方法
1.首先,在类中定义一个静态的方法,如学生类中定义一个比较年龄的静态类
package com.fqw.method1reference;
public class Student {
//姓名、年龄、身高
private String name;
private int age;
private double height;
public Student() {}
public Student(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public static int compareAge(Student s1, Student s2){
return s1.getAge() - s2.getAge();
}
}
2.接着,使用静态方法引用
package com.fqw.method1reference;
import java.util.Arrays;
public class Demo1{
public static void main(String[] args) {
Student[] students=new Student[6];
students[0]=new Student("张三", 18, 1.75);
students[1]=new Student("李四", 23, 1.80);
students[2]=new Student("王五", 20, 1.65);
students[3]=new Student("赵六", 17, 1.70);
students[4]=new Student("钱七", 22, 1.65);
students[5]=new Student("孙八", 16, 1.75);
//输出排序前的学生数组
for (int i=0;i<students.length;i++){
Student student = students[i];
System.out.println(student.getName()+","+ student.getAge()+","+ student.getHeight());
}
//Arrays.sort(students, ( o1, o2) -> o1.getAge()-o2.getAge);
//Arrays.sort(students, ( o1, o2) -> Student.compareAge(o1,o2));
//静态方法引用 类名::静态方法
Arrays.sort(students, Student::compareAge);
System.out.println("=======================================");
//输出排序后的学生数组
for (int i=0;i<students.length;i++){
Student student = students[i];
System.out.println(student.getName()+","+ student.getAge()+","+ student.getHeight());
}
}
}
2.2 实例方法引用
对象名::实例方法
使用场景:如果某个Lambda表达式里只是通过对象名称调用一个实例方法,并且“->”前后参数的形式一致,就可以使用实例方法
package com.fqw.method1reference;
public class Student {
//姓名、年龄、身高
private String name;
private int age;
private double height;
public Student() {}
public Student(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public int compareByHeight(Student o1,Student o2){
return Double.compare(o1.getHeight(), o2.getHeight());
}
}
package com.fqw.method1reference;
import java.util.Arrays;
public class Demo2 {
public static void main(String[] args) {
Student[] students=new Student[6];
students[0]=new Student("张三", 18, 1.75);
students[1]=new Student("李四", 23, 1.80);
students[2]=new Student("王五", 20, 1.65);
students[3]=new Student("赵六", 17, 1.70);
students[4]=new Student("钱七", 22, 1.65);
students[5]=new Student("孙八", 16, 1.75);
//输出排序前的学生数组
for (int i=0;i<students.length;i++){
Student student = students[i];
System.out.println(student.getName()+","+ student.getAge()+","+ student.getHeight());
}
//实例方法引用 对象名::静态方法
Student t=new Student();
//Arrays.sort(students, ( o1, o2) -> t.compareByHeight(o1,o2));
Arrays.sort(students, t::compareByHeight);
System.out.println("=======================================");
//输出排序后的学生数组
for (int i=0;i<students.length;i++){
Student student = students[i];
System.out.println(student.getName()+","+ student.getAge()+","+ student.getHeight());
}
}
}
2.3 特定类型方法的引用
特定类的名称::方法
使用场景:如果某个Lambda表达式里只调用一个特定类型的实例方法,并且前面参数列表中的第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法的入参的,则此时就可以使用特定类型的方法引用
场景:有一个字符串数组,里面有一些人的名字,都是英文名字,请按照名字的英文首字母排序,忽略首字母大小
import java.util.Arrays;
import java.util.Comparator;
public class Demo3 {
public static void main(String[] args) {
//有一个字符串数组,里面有一些人的名字,都是英文名字,请按照名字的英文首字母排序,忽略首字母大小
String[] names=new String[]{"Tom","lisi","angela","zhaoliu","Mike","Jack","Bob","Andy","caocao","Rose"};
// Arrays.sort(names, new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o1.compareToIgnoreCase(o2);
// }
// });
//Arrays.sort(names, (o1,o2)->o1.compareToIgnoreCase(o2));
Arrays.sort(names, String::compareToIgnoreCase);
System.out.println(Arrays.toString(names));
}
}
2.4 构造器引用
类名::new
使用场景:如果某个Lambda表达式里只是创建对象,并且“->”前后参数的形式一致,就可以使用构造器引用
package com.fqw.method1reference;
public class Demo4 {
public static void main(String[] args) {
// CarFactory cf=new CarFactory() {
// @Override
// public Car getCar(String name){
// return new Car(name);
// }
// };
// CarFactory cf=name -> new Car(name);
CarFactory cf=Car:: new ;
Car c1=cf.getCar("奥迪");
System.out.println(c1.getName());
}
}
interface CarFactory{
Car getCar(String name);
}
class Car{
private String name;
public Car() {
}
public Car(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}