《Java 程序设计》第 10 章 - 接口与 Lambda 表达式
大家好!今天我们来学习 Java 中的接口与 Lambda 表达式。这两个特性在 Java 编程中非常重要,尤其是 Lambda 表达式,它是 Java 8 引入的新特性,极大地简化了代码编写。话不多说,让我们开始吧!
思维导图
10.1 接口
10.1.1 接口定义
接口(interface)是纯粹的抽象类型,所有成员默认 public
(JDK 8 以前)。
语法:
[public] interface 接口名 [extends 父接口列表] {// 1. 抽象方法(可省略 public abstract)返回类型 方法名(参数列表);// 2. 常量(默认 public static final)类型 常量名 = 值;
}
10.1.2 接口的实现
类通过 implements
关键字实现接口,必须实现所有抽象方法。
示例:
// 文件:Drawable.java
public interface Drawable {void draw(); // 抽象方法String DEFAULT_COLOR = "BLACK"; // 常量
}// 文件:Circle.java
public class Circle implements Drawable {private double r;public Circle(double r) { this.r = r; }@Overridepublic void draw() {System.out.println("画一个半径为 " + r + " 的圆,颜色:" + DEFAULT_COLOR);}public static void main(String[] args) {new Circle(3.5).draw();}
}
10.1.3 接口的继承
接口可多重继承,形成“继承链”。
interface A { void a(); }
interface B { void b(); }
interface C extends A, B { void c(); } // C 拥有 a() b() c()
10.1.4 接口类型的使用
接口变量可指向任何实现类对象——多态的核心。
Drawable d = new Circle(2);
d.draw(); // 运行时调用 Circle 的实现
10.1.5 常量
接口中的变量默认 public static final
,一般用于全局常量池。
10.2 静态方法和默认方法
10.2.1 静态方法
JDK 8 起允许接口定义 static
方法,只能通过接口名调用。
public interface Logger {static void info(String msg) {System.out.println("[INFO] " + msg);}
}
// 调用
Logger.info("系统启动");
10.2.2 默认方法
default
方法提供默认实现,实现类可重写也可直接使用。
public interface Animal {default void breathe() {System.out.println("用肺呼吸");}
}
10.2.3 解决默认方法冲突
当类实现的多个接口出现同名默认方法时,遵循以下规则:
类优先(优先选择父类的方法);
否则必须手动重写,使用
接口名.super.方法()
指定。
interface A {default void hello() { System.out.println("A"); }
}
interface B {default void hello() { System.out.println("B"); }
}
class C implements A, B {@Overridepublic void hello() {A.super.hello(); // 显式指定}
}
10.3 接口示例
10.3.1 Comparable 接口
让对象具备自然排序能力。
import java.util.*;public class Student implements Comparable<Student> {private String name;private int score;public Student(String name, int score) {this.name = name;this.score = score;}@Overridepublic int compareTo(Student o) {return Integer.compare(this.score, o.score); // 升序}@Overridepublic String toString() {return name + ":" + score;}public static void main(String[] args) {// 1. 用 new ArrayList<>(...) 包装,变成可变列表List<Student> list = new ArrayList<>(List.of(new Student("Alice", 90),new Student("Bob", 70),new Student("Carol", 95)));// 2. 排序Collections.sort(list);// 3. 打印list.forEach(System.out::println);}
}
10.3.2 Comparator 接口
当需要多种排序策略时使用,更灵活。
// 文件:Student.java
import java.util.*;public class Student implements Comparable<Student> {private String name;private int score;public Student(String name, int score) {this.name = name;this.score = score;}/* ===== 新增 getter ===== */public String getName() { return name; }public int getScore() { return score; }@Overridepublic int compareTo(Student o) {return Integer.compare(this.score, o.score); // 自然排序:分数升序}@Overridepublic String toString() {return name + ":" + score;}
}
// 文件:StudentComparator.java
import java.util.*;public class StudentComparator {public static void main(String[] args) {// 用 new ArrayList<>(...) 把不可变列表变成可变List<Student> list = new ArrayList<>(List.of(new Student("Alice", 90),new Student("Bob", 70),new Student("Carol", 95)));// 1. 按姓名升序list.sort(Comparator.comparing(Student::getName));System.out.println("按姓名升序:");list.forEach(System.out::println);// 2. 按分数降序list.sort(Comparator.comparingInt(Student::getScore).reversed());System.out.println("按分数降序:");list.forEach(System.out::println);}
}
10.4 Lambda 表达式
10.4.1 Lambda 简介
Lambda 是匿名函数,用于简化函数式接口的实例化。
10.4.2 函数式接口
仅含一个抽象方法的接口,可用 @FunctionalInterface
标注。
10.4.3 Lambda 语法
(parameters) -> { statements }
10.4.4 预定义的函数式接口
接口名 | 抽象方法 | 用途示例 |
---|---|---|
Predicate<T> | boolean test(T t) | 过滤 |
Function<T,R> | R apply(T t) | 转换 |
Consumer<T> | void accept(T t) | 消费 |
Supplier<T> | T get() | 生产 |
10.4.5 方法引用与构造方法引用
方法引用:
类::方法
构造引用:
类::new
示例:使用 Lambda + 方法引用过滤列表
import java.util.*;
import java.util.function.Predicate;public class LambdaDemo {public static void main(String[] args) {List<String> list = Arrays.asList("Java", "Python", "Go", "C++");// 1. 匿名类Predicate<String> p1 = new Predicate<String>() {@Overridepublic boolean test(String s) {return s.length() > 3;}};// 2. LambdaPredicate<String> p2 = s -> s.length() > 3;// 3. 方法引用Predicate<String> p3 = LambdaDemo::isLongName;list.stream().filter(p3).forEach(System.out::println);}private static boolean isLongName(String s) {return s.length() > 3;}
}
10.5 小结
接口:解耦、多继承、常量池;
默认/静态方法:增强接口能力;
Comparable & Comparator:两种排序策略;
Lambda:函数式编程利器,简化代码。
编程练习
练习 1:员工薪资处理(接口 + Lambda)
需求:
定义接口
Payable
含double calcSalary()
;类
SalariedEmployee
(固定工资)、HourlyEmployee
(时薪)实现接口;用 Lambda 计算全体员工总薪资并过滤高薪员工(> 20000)。
完整代码:
// 文件:Payable.java
@FunctionalInterface
public interface Payable {double calcSalary();
}// 文件:SalariedEmployee.java
public class SalariedEmployee implements Payable {private double monthSalary;public SalariedEmployee(double monthSalary) {this.monthSalary = monthSalary;}@Overridepublic double calcSalary() {return monthSalary;}@Overridepublic String toString() {return "SalariedEmployee: " + monthSalary;}
}// 文件:HourlyEmployee.java
public class HourlyEmployee implements Payable {private double hours;private double rate;public HourlyEmployee(double hours, double rate) {this.hours = hours;this.rate = rate;}@Overridepublic double calcSalary() {return hours * rate;}@Overridepublic String toString() {return "HourlyEmployee: " + calcSalary();}
}// 文件:PayrollSystem.java
import java.util.*;
import java.util.stream.Collectors;public class PayrollSystem {public static void main(String[] args) {List<Payable> employees = List.of(new SalariedEmployee(18000),new HourlyEmployee(160, 100),new SalariedEmployee(25000),new HourlyEmployee(200, 120));// 总薪资double total = employees.stream().mapToDouble(Payable::calcSalary).sum();System.out.println("总薪资 = " + total);// 高薪员工List<Payable> high = employees.stream().filter(e -> e.calcSalary() > 20000).collect(Collectors.toList());high.forEach(System.out::println);}
}
运行结果:
① 思维导图:接口与 Lambda 学习路线
② 接口继承关系类图(PlantUML)
@startuml
interface Drawable
interface Colorable
interface Paintable extends Drawable, Colorable
class Circle implements Paintable
@enduml
③ Lambda 表达式执行流程图
结语
复制代码直接 javac *.java
即可体验。欢迎评论区交流!