《Java 程序设计》第 10 章 - 接口与 Lambda 表达式

发布于:2025-07-31 ⋅ 阅读:(19) ⋅ 点赞:(0)

        大家好!今天我们来学习 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; }

    @Override
    public 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 解决默认方法冲突

当类实现的多个接口出现同名默认方法时,遵循以下规则:

  1. 类优先(优先选择父类的方法);

  2. 否则必须手动重写,使用 接口名.super.方法() 指定。

interface A {
    default void hello() { System.out.println("A"); }
}
interface B {
    default void hello() { System.out.println("B"); }
}
class C implements A, B {
    @Override
    public 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;
    }

    @Override
    public int compareTo(Student o) {
        return Integer.compare(this.score, o.score); // 升序
    }

    @Override
    public 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; }

    @Override
    public int compareTo(Student o) {
        return Integer.compare(this.score, o.score); // 自然排序:分数升序
    }

    @Override
    public 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>() {
            @Override
            public boolean test(String s) {
                return s.length() > 3;
            }
        };

        // 2. Lambda
        Predicate<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)

需求:

  1. 定义接口 Payabledouble calcSalary()

  2. SalariedEmployee(固定工资)、HourlyEmployee(时薪)实现接口;

  3. 用 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;
    }
    @Override
    public double calcSalary() {
        return monthSalary;
    }
    @Override
    public 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;
    }
    @Override
    public double calcSalary() {
        return hours * rate;
    }
    @Override
    public 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 即可体验。欢迎评论区交流!


网站公告

今日签到

点亮在社区的每一天
去签到