Java 函数
最后修改于 2024 年 7 月 7 日
在本文中,我们将展示如何在 Java 中使用 Function 接口。该接口位于 java.util.function 包中。
函数定义
Function 是一个 Java 函数式接口,它表示接受一个参数并产生结果的函数。 它是一个具有单个抽象方法的接口,该方法接受输入,执行特定任务,并可以选择返回输出。
由于 Java 不支持纯函数,因此引入了该接口以克服此限制。 实际上,编译器会在后台从每个 Function 创建一个对象。
Function 用于在类外部创建小型纯函数和匿名表达式,尤其是在函数式编程范例中。
简单 Function 示例
以下是使用 Function 的一个简单示例。
import java.util.function.Function;
import java.util.List;
Function<Integer, Integer> doubleNum = n -> n * 2;
void main() {
var vals = List.of(1, 2, 3, 4, 5 );
for (var e: vals) {
int res = doubleNum.apply(e);
System.out.println(res);
}
}
我们定义一个将值乘以 2 的函数。
Function<Integer, Integer> doubleNum = n -> n * 2;
该函数声明了两种类型。 第一个类型是函数输入的类型,第二个类型是函数结果的类型。 等号后面的变量是输入变量。 -> 之后的表达式的结果是函数返回值。 请注意,我们已经在类外部定义了一个函数。
var vals = List.of(1, 2, 3, 4, 5 );
for (var e: vals) {
int res = doubleNum.apply(e);
System.out.println(res);
}
我们遍历整数列表,并将函数应用于每个元素。
$ java Main.java 2 4 6 8 10
在下一个示例中,我们使用 Function 定义一个问候语。
import java.util.List;
import java.util.function.Function;
Function<String, String> greet = name -> String.format("Hello %s!", name);
void main() {
var names = List.of("Peter", "Lucia", "Jozef", "Martin");
for (var name : names) {
String greeting = greet.apply(name);
System.out.println(greeting);
}
}
示例中的函数接受一个名称作为参数,并返回一个问候语。
Function<String, String> greet = name -> String.format("Hello %s!", name);
我们使用模板字符串从“Hello”文字和 name 输入变量构造问候语。
$ java Main.java Hello Peter! Hello Lucia! Hello Jozef! Hello Martin!
删除重复项
在下一个示例中,我们从整数列表中删除重复项。
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.List;
Function<List<Integer>, List<Integer>> remDup = vals -> vals.stream()
.distinct()
.collect(Collectors.toList());
void main() {
var nums = List.of(1, 2, 1, 2, 3, 4, 5, 1, 0, -1 );
var nums2 = remDup.apply(nums);
System.out.println(nums2);
}
我们有一个整数列表。 我们定义 remDup 函数以删除重复值。
Function<List<Integer>, List<Integer>> remDup = vals -> vals.stream()
.distinct()
.collect(Collectors.toList());
该函数接受一个整数列表作为输入,并返回一个整数列表。 在其主体中,它使用 distinct 方法来完成这项工作。
$ java Main.java [1, 2, 3, 4, 5, 0, -1]
过滤器中的函数
stream filter 方法期望一个谓词函数。 我们有一种特殊的函数类型,称为 Predicate。 但是我们也可以使用 Function。
import java.util.function.Function;
import java.util.List;
Function<Integer, Boolean> isEven = n -> n % 2 == 0;
void main() {
var vals = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9);
vals.stream().filter(isEven::apply).forEach(System.out::println);
}
我们定义 isEven 函数,如果该数字是偶数,则返回 true。
vals.stream().filter(isEven::apply).forEach(System.out::println);
我们传递对函数 apply 方法的引用。
$ java Main.java 2 4 6 8
谓词函数将定义如下
Predicate<Integer> isEven = n -> n % 2 == 0;
计算年龄
以下示例使用函数来计算用户的年龄。
import java.time.LocalDate;
import java.time.Period;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
Function<LocalDate, Integer> findAge = dob -> Period.between(dob,
LocalDate.now()).getYears();
Consumer<User> action = u -> System.out.printf("%s is %d years old%n",
u.name, findAge.apply(u.dob));
void main() {
List<User> users = List.of(
User.of("John Doe", "gardener", LocalDate.of(1956, 11, 12)),
User.of("Roger Roe", "driver", LocalDate.of(1976, 11, 12)),
User.of("John Doe", "teacher", LocalDate.of(1967, 1, 7)),
User.of("John Doe", "gardener", LocalDate.of(1998, 5, 22)));
users.forEach(action);
}
record User(String name, String occupation, LocalDate dob) {
static User of(String name, String occupation, LocalDate dob) {
return new User(name, occupation, dob);
}
}
我们有一个用户列表。 每个用户都有一个出生日期作为字段。 我们在函数中从此字段计算用户的年龄。
Function<LocalDate, Integer> findAge = dob -> Period.between(dob,
LocalDate.now()).getYears();
该函数使用一个简单的表达式,其中使用 Period.between 方法计算年龄。
Consumer<User> action = u -> System.out.printf("%s is %d years old%n",
u.name, findAge.apply(u.dob));
我们定义一个消费者,为每个元素打印消息。
$ java Main.java John Doe is67 years old Roger Roe is47 years old John Doe is57 years old John Doe is25 years old
年龄分类
下一个示例使用带有 switch 表达式的函数。
import java.util.function.Function;
import java.util.function.Consumer;
import java.util.List;
Function<Integer, String> ageCategory = age -> switch (age) {
case Integer n when n < 18 && n > 0 -> "minor";
case Integer n when n >= 18 && n < 64 -> "adult";
case Integer n when n > 64 -> "senior";
default -> "n/a";
};
Consumer<String> action = System.out::println;
void main() {
List<Integer> ages = List.of(11, 18, 17, 19, 21, 55, 86, 99, 43, 65, 63);
ages.stream().map(age -> ageCategory.apply(age)).forEach(action);
}
我们有一个年龄值列表。 我们将每个值放入一个类别:未成年人、成年人和老年人。
$ java Main.java minor adult minor adult adult adult senior senior adult senior adult
键提取器 Function
我们定义一个函数,用作比较器的键提取器。
import java.util.function.Function;
import java.util.Comparator;
import java.util.List;
Function<String, Integer> strLen = String::length;
void main() {
var words = List.of("peculiar", "up", "blue", "atom", "by", "nice",
"storm", "edible", "sky", "bookworm", "stronghold");
words.forEach(System.out::println);
System.out.println("----------------------");
var sorted = words.stream().sorted(Comparator.comparing(strLen)).toList();
sorted.forEach(System.out::println);
}
我们有一个单词列表。 我们按单词的长度对单词进行排序。
Function<String, Integer> strLen = String::length;
该函数返回传递值的字符串长度。 它用作比较器对象的键。
var sorted = words.stream().sorted(Comparator.comparing(strLen)).toList();
我们将键提取器函数传递给 Comparator.comparing,该函数为 sorted 方法生成一个比较器。
$ java Main.java peculiar up blue atom by nice storm edible sky bookworm stronghold ---------------------- up by sky blue atom nice storm edible peculiar bookworm stronghold
函数组合
函数可以使用 compose 和 andThen 方法进行组合。 区别在于调用函数的顺序。
import java.util.function.Function;
void main() {
Function<String, String> upperFun = String::toUpperCase;
Function<String, String> reverseFun = val -> new StringBuilder(val).reverse().toString();
var res = upperFun.compose(reverseFun).apply("falcon");
System.out.println(res);
}
compose 函数将 reverseFun 和 upperFun 应用于参数。
$ java Main.java NOCLAF
有时,函数组合的顺序很重要。
import java.util.function.Function;
void main() {
Function<Double, Double> half = a -> a / 2;
Function<Double, Double> square = a -> a * a;
Function<Double, Double> squareAndThenHalf = square.andThen(half);
Double res1 = squareAndThenHalf.apply(3d);
System.out.println(res1);
Function<Double, Double> squareComposeHalf = square.compose(half);
Double res2 = squareComposeHalf.apply(3d);
System.out.println(res2);
}
我们有两个方法 half 和 square。
Function<Double, Double> squareAndThenHalf = square.andThen(half); Double res1 = squareAndThenHalf.apply(3d);
andThen 方法首先求平方,然后减半。
Function<Double, Double> squareComposeHalf = square.compose(half); Double res2 = squareComposeHalf.apply(3d);
compose 方法首先减半,然后求平方。
$ java Main.java 4.5 2.25
在下一个示例中,我们将三个函数组合在一起。
import java.util.function.Function;
Function<Integer, Integer> fn1 = n -> n + 1;
Function<Integer, Integer> fn2 = n -> n * 2;
Function<Integer, Integer> fn3 = n -> n * n;
Function<Integer, Integer> cfn1 = fn1.andThen(fn2).andThen(fn3);
Function<Integer, Integer> cfn2 = fn1.compose(fn2).compose(fn3);
void main() {
Integer res = cfn1.apply(10);
System.out.println(res);
Integer res2 = cfn2.apply(10);
System.out.println(res2);
}
首先,我们使用 andThen 组合这三个函数,然后使用 compose 组合。
$ java Main.java 484 201
将 Function 作为参数传递
在以下示例中,我们将定义的 Function 作为参数传递给另一个函数。
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
void main() {
List<String> words = List.of("coin", "pen", "Monday", "NASA", "Moscow",
"cup", "notebook", "class");
Function<String, String> uf = String::toUpperCase;
Function<String, String> lf = String::toLowerCase;
var res = modify(words, uf);
System.out.println(res);
var res2 = modify(words, lf);
System.out.println(res2);
}
List<String> modify(List<String> data, Function<String, String> f) {
var uppered = new ArrayList<String>();
for (var e : data) {
uppered.add(f.apply(e));
}
return uppered;
}
在该程序中,我们有一个单词列表。 我们定义 modify 方法,该方法接受一个函数作为参数。 该函数转换数组元素,并返回一个修改后的字符串新列表。
Function<String, String> uf = String::toUpperCase; Function<String, String> lf = String::toLowerCase;
我们有两个函数。 它们将传递的单词大写和小写。
List<String> modify(List<String> data, Function<String, String> f) {
modify 函数接受一个函数作为第二个参数。
$ java Main.java [COIN, PEN, MONDAY, NASA, MOSCOW, CUP, NOTEBOOK, CLASS] [coin, pen, monday, nasa, moscow, cup, notebook, class]
来源
在本文中,我们使用了 Java Function 接口。
作者
列出所有Java教程。