1. 概述

钳子函数限制一个值在特定范围内。它确保给定的值不会超出特定的下限和上限。

在这个教程中,我们将通过示例探讨如何在Java中实现钳子函数。

2. Java 21之前钳子函数的实现

在Java 21之前,Java并没有内置的钳子函数。我们需要自己编写这个函数。

钳子函数定义了一个值范围。低于最小值的值设置为最小值,超过最大值的值设置为最大值。同时,处于范围内的值则返回自身。

2.1. 使用方法重载

我们可以使用方法重载(method overloading)为不同数据类型实现钳子函数

让我们创建一个Clamp类,并添加一个返回整数的clamp()方法:

class Clamp {  
    int clamp(int value, int min, int max) {
        return Math.max(min, Math.min(max, value));
    }   
}

这里,我们创建了一个接受值、下限和上限作为参数的clamp()方法。此外,我们使用Math类来设置最小值和最大值。最后,如果值在设定范围内,就返回该值,否则返回最小值或最大值。

接下来,我们为clamp()方法编写单元测试:

@Test
void givenValueOutsideRange_whenClamp_thenReturnLowerValue() {
    Clamp clampValue = new Clamp();
    assertEquals(15, clampValue.clamp(10, 15, 35));
}

这里,我们创建一个Clamp实例并调用clamp()方法。值设为10,下限设为15,上限设为35。由于值不在范围内,方法返回最小值。

这里是值在范围内的测试:

assertEquals(20, clampValue.clamp(20, 15, 35));

因为输入值在范围内,clamp()方法返回这个值。

最后,看一个值大于最大值的测试:

assertEquals(35, clampValue.clamp(50, 15, 35));

在这里,输入值超过了最大边界。因此,clamp()方法返回最大值。

此外,我们还可以为double数据类型重载clamp()方法:

double clamp(double value, double min, double max) {
    return Math.max(min, Math.min(max, value));
}

这里,我们为double数据类型重载了clamp()方法,它返回一个double

2.2. 使用泛型

此外,我们可以使用泛型(generics)使clamp()方法更加灵活,适用于不同数据类型

static <T extends Comparable<T>> T clamp(T value, T min, T max) {
    if (value.compareTo(min) < 0) {
        return min;
    } else if (value.compareTo(max) > 0) {
        return max;
    } else {
        return value;
    }
}

上述方法接受三个泛型类型T作为参数,T实现了Comparable接口。然而,这种方法可能会有些昂贵。如果最小值和最大值是基本类型,Java会自动将它们装箱成相应的对象,因为基本类型不能实现Comparable接口

现在,我们为泛型方法编写单元测试:

@Test
void givenFloatValueWithinRange_whenClamp_thenReturnValue() {
    Clamp clampValue = new Clamp();
    assertEquals(16.2f, clampValue.clamp(16.2f, 15f, 35.3f));
}

这个方法接受float类型,但在计算值时,它将float转换为Float,然后将返回值从Float反向转换为float。因此,我们有两次装箱操作和一次解包操作。

推荐使用方法重载以避免装箱/拆箱操作

3. Java 21之后的钳子函数

Java 21,目前仍处于实验阶段,将在Math类中引入clamp()方法。这使得在不编写自定义方法的情况下轻松钳制值变得简单。

以下是使用Java 21中clamp()方法的一个例子:

@Test
void givenValueWithinRange_whenClamp_thenReturnValue() {
    assertEquals(20, Math.clamp(20, 17, 98));
}

在上面的代码中,我们调用clamp()方法并设置最小值和最大值。由于值在最小值和最大值之间,代码返回该值。

值得注意的是,clamp()方法支持不同的数据类型,因此不需要为不同数据类型进行显式实现。

4. 总结

在这篇文章中,我们学习了三种在Java中实现钳子函数的方法。我们了解了在Java 21引入标准库中的clamp()方法之前如何编写clamp()方法。

如往常一样,所有示例的源代码可以在GitHub上找到。