Java 接口演进:最佳实践与策略

发布: (2025年12月27日 GMT+8 19:29)
8 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的完整文本内容(除代码块和 URL 之外),我将为您翻译成简体中文并保留原有的 Markdown 格式。

Source:

Java 接口的演进 – 自 Java 8 起的 LTS 发行版

发行版日期
Java 82014 年 3 月
Java 112018 年 9 月
Java 172021 年 9 月
Java 212023 年 9 月

📍 2014 年 3 月 – Java 8

函数式革命

Java 8 通过允许 非抽象方法 改变了接口的设计。这解决了 接口演进问题,并通过 函数式接口 为函数式编程打开了大门。

新的接口特性

  • 默认方法 – 在接口内部提供具体实现(使用 default 关键字)。
  • 静态方法 – 在接口本身上定义的工具方法。
  • 函数式接口 – 仅包含一个抽象方法的接口,可与 lambda 表达式一起使用。

1. 默认方法

public interface MyInterface {
    void existingMethod();

    default void newDefaultMethod() {
        System.out.println("This is a default method.");
    }
}
public class MyClass implements MyInterface {
    @Override
    public void existingMethod() {
        System.out.println("Implementing existing method.");
    }

    // No need to implement newDefaultMethod() unless a custom behavior is required
    public void someMethod() {
        newDefaultMethod();   // calls the default implementation
    }
}

实际案例

java.util.Listsort

public interface List extends Collection {
    default void sort(Comparator c) {
        Collections.sort(this, c);
    }
}

// Usage
List list = new ArrayList<>(Arrays.asList("banana", "apple", "cherry"));
list.sort(Comparator.naturalOrder());
System.out.println(list);   // → [apple, banana, cherry]

java.lang.IterableforEach

public interface Iterable {
    default void forEach(Consumer action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
}

// Usage
List list = Arrays.asList("apple", "banana", "cherry");
list.forEach(item -> System.out.println(item));

java.util.Map – 多个默认方法

public interface Map {
    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key)) ? v : defaultValue;
    }

    default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }
        return v;
    }

    // … other default methods: remove, replace, compute, merge, …
}

// Usage
Map map = new HashMap<>();
map.put("apple", 1);
Integer i = map.getOrDefault("banana", 0);
System.out.println(i);   // → 0

2. 接口中的静态方法

public interface MyInterface {
    static void utilityMethod() {
        System.out.println("This is a static method in the interface.");
    }
}
public class MyClass implements MyInterface {
    public void someMethod() {
        MyInterface.utilityMethod();   // 调用静态方法
    }
}

Utility‑class 示例 – java.util.Collections

public final class Collections {
    public static List emptyList() {
        return (List) EMPTY_LIST;
    }
}

// 用法
List empty = Collections.emptyList();
System.out.println(empty);   // → []

Map 上的工厂方法

public interface Map {
    static Map of() {
        return new HashMap<>();
    }

    static Map of(K k1, V v1) {
        Map map = new HashMap<>();
        map.put(k1, v1);
        return map;
    }

    // … 更多条目的重载 …
}

// 用法
Map map = Map.of("apple", 1, "banana", 2);
System.out.println(map);   // → {apple=1, banana=2}

3. 函数式接口

@FunctionalInterface
public interface MyFunctionalInterface {
    void singleAbstractMethod();
}

// Lambda 用法
MyFunctionalInterface func = () -> System.out.println("Lambda expression implementation.");
func.singleAbstractMethod();   // → Lambda expression implementation.

java.util.function

接口用途示例
Predicate接受一个参数并返回布尔值的函数Predicate<String> isEmpty = s -> s.isEmpty();
Function接受一个参数并返回结果的函数Function<String,Integer> length = s -> s.length();
Consumer对单个输入参数执行操作Consumer<String> printer = System.out::println;

Predicate

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

// Usage
Predicate<String> isEmpty = str -> str.isEmpty();
System.out.println(isEmpty.test(""));      // → true
System.out.println(isEmpty.test("hello")); // → false

Function

@FunctionalInterface
public interface Function<T,R> {
    R apply(T t);
}

// Usage
Function<String,Integer> stringLength = s -> s.length();
System.out.println(stringLength.apply("hello")); // → 5
System.out.println(stringLength.apply(""));      // → 0

Consumer

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

// Usage
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello, world!");   // → Hello, world!

Java 中的函数式接口

java.util.function.Consumer

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

// Usage
Consumer<String> print = str -> System.out.println(str);
print.accept("hello");   // → hello
print.accept("world");   // → world

java.util.function.Supplier

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

// Usage
Supplier<Double> randomValue = () -> Math.random();
System.out.println(randomValue.get()); // → Random double value
System.out.println(randomValue.get()); // → Another random double value

java.util.function.UnaryOperator (extends Function)

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T,T> {}

// Usage
UnaryOperator<Integer> square = x -> x * x;
System.out.println(square.apply(5)); // → 25
System.out.println(square.apply(0)); // → 0

java.util.function.BinaryOperator (extends BiFunction)

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {}

// Usage
BinaryOperator<Integer> add = (x, y) -> x + y;
BinaryOperator<Integer> addWithMethodReference = Integer::sum;
System.out.println(add.apply(5, 10));                 // → 15
System.out.println(addWithMethodReference.apply(0,0)); // → 0

Java 8 之前的函数式风格接口

InterfacePackageAbstract Method常见用途
Runnablejava.langvoid run()线程、执行器
Callablejava.util.concurrentV call()带返回值的线程
Comparatorjava.utilint compare(T a, T b)排序
ActionListenerjava.awt.eventvoid actionPerformed(ActionEvent e)GUI 事件

这些接口是在 Java 8 之前定义的,后来被认定为函数式接口。

接口特性演进

2018 年 9 月 – Java 11

特性: 接口中的私有方法(在 Java 9 中引入,Java 11 继续支持)。

public interface DataProcessor {

    default void process(String data) {
        String cleaned = sanitize(data);
        System.out.println("Processed: " + cleaned);
    }

    private String sanitize(String input) {
        return input.trim().toLowerCase();
    }

    static DataProcessor create() {
        return new DataProcessor() {};
    }
}

// Usage
public class TestInterfacePrivateMethod {
    public static void main(String[] args) {
        DataProcessor processor = DataProcessor.create();
        processor.process("  HELLO WORLD  "); // → Processed: hello world
    }
}

Java 11 是一个 LTS 版本,但除私有方法支持外并未添加新的接口功能。

2021 年 9 月 – Java 17

特性: 密封接口(与密封类一起)。

// Sealed interface – only the listed types may implement it.
public sealed interface Shape permits Circle, Rectangle, Polygon {}

// Final class – cannot be subclassed.
final class Circle implements Shape {
    public final double radius;
    public Circle(double radius) { this.radius = radius; }
}

// Sealed subclass – continues the restriction.
sealed class Polygon implements Shape permits Quadrilateral {
    public final int sides;
    public Polygon(int sides) { this.sides = sides; }
}

// Non‑sealed class – can be freely extended.
non-sealed class Rectangle implements Shape {
    public final double width, height;
    public Rectangle(double w, double h) { width = w; height = h; }
}

规则

  • 子类型必须在 permits 子句中列出(或位于同一源文件中)。
  • 每个被允许的子类型必须声明为 finalsealednon‑sealed
  • non‑sealed 只能用于直接扩展密封类型的类(或接口)。

2023 年 9 月 – Java 21

特性: switch 的模式匹配(没有新的专门针对接口的特性)。

sealed interface Expr permits Constant, Add {}

record Constant(int value) implements Expr {}
record Add(Expr left, Expr right) implements Expr {}

public static int eval(Expr e) {
    return switch (e) {
        case Constant c -> c.value;
        case Add a      -> eval(a.left()) + eval(a.right());
    };
}

// Usage
Expr expr = new Add(new Constant(3), new Constant(5));
System.out.println(eval(expr)); // → 8

接口设计最佳实践

  • 谨慎使用默认方法 – 它们应当提供可选功能,而不是改变核心契约。
  • 考虑对现有实现的影响 – 添加默认方法可能会影响所有当前的实现者。
  • 倾向于使用静态方法来提供工具 – 使其与接口的目的紧密相关。
  • 清晰地为默认方法和静态方法编写文档 – 说明其意图、用法以及任何副作用。
  • 避免在接口中使用状态 – 默认方法应当操作实现类的状态,而不是维护自己的状态。

Closing Thought

静态应用安全测试(SAST)仍然是成熟的安全软件开发生命周期(SSDLC)的基石。它提供早期、可操作的洞察,降低风险,赋能开发者,并在部署前强化应用程序。当 SAST 从单纯的检测工具转变为长期安全工程文化的催化剂时,整个开发生态系统都将受益。

Back to Blog

相关文章

阅读更多 »