lambda 表达式是否具有无法通过其他东西复制的独特用途?
Do lambda expressions have a unique use that cannot be replicated through something else?
我今天在学习 lambda 表达式,到目前为止,我还没有发现它们的独特用途。所以我问自己,它们是否不仅仅是一个取自函数式语言的方便的小工具。
什么是 Lambda 表达式?
根据我的阅读,它们可用于实例化匿名 类、传递简单方法和过滤集合中的元素。
例如:
List<Integer> list = List.of(1, 2, 3, 4, 5);
for (int i : list)
System.out.println(i);
...可以写成:
List<Integer> list = List.of(1, 2, 3, 4, 5);
list.forEach((i) -> {
System.out.println(i);
});
但是你为什么要那样做?每次你写下一个 lambda 表达式时,你可以只把原始表达式放在那里!
public class Test {
@FunctionalInterface
interface BoolOperation {
boolean execute(boolean param1, boolean param2);
}
static void test(BoolOperation op, boolean param1, boolean param2) {
System.out.println(op.execute(param1, param2));
}
public static void main(String[] args) {
test((x, y) -> x && y, true, false);
test((x, y) -> x || y, false, true);
test((x, y) -> x == y, true, true);
}
}
...与以下内容相同:
public class Test {
public static void main(String[] args) {
System.out.println(true && false);
System.out.println(false || true);
System.out.println(true == true);
}
}
和
public class Test {
public static void main(String[] args) {
Runnable r = () -> {
System.out.println("Running.");
};
}
}
... shorter/readable 并不比
public class Test {
public static void main(String[] args) {
Runnable r = new ThreadThingy();
}
}
class ThreadThingy implements Runnable {
public void run() {
System.out.println("Running.");
}
}
我读过 Oracle-Docs,但它们非常具体,没有突出一般用途。
当然,你的布尔表达式的例子直接写起来更容易。但是如果你想为不同的布尔表达式创建真值表呢?
public class Test {
@FunctionalInterface
interface BoolOperation {
boolean execute(boolean param1, boolean param2);
}
static void line(String s, BoolOperation op, boolean b1, boolean b2) {
System.out.printf("%b %s %b = %b%n", b1, s, b2, op.execute(b1, b2));
}
static void table(String s, BoolOperation op) {
System.out.println("Truth table for "+s);
line(s, op, false, false);
line(s, op, false, true);
line(s, op, true, false);
line(s, op, true, true);
System.out.println();
}
public static void main(String[] args) {
table("AND", (x, y) -> x && y);
table("OR", (x, y) -> x || y);
table("XNOR", (x, y) -> x == y);
table("XOR", (x, y) -> x != y);
table("implies", (x, y) -> !x || y);
}
}
对于各种操作,您仍然可以使用匿名 classes 来做到这一点,但是这将需要更多的“样板” - 需要编写的代码只是为了满足编译器而不增加任何价值。
例如,您可以这样编写第一个 table
调用:
table("AND", new BoolOperation() {
@Override
public boolean execute(boolean param1, boolean param2) {
return param1 && param2;
}
});
但你不能告诉我这比
更具可读性和更容易理解
table("AND", (x, y) -> x && y);
对于一个更“真实世界”的例子,我的一个项目包含一个 class ObjectCsvWriter
,它需要一个 PrintWriter
和一个 Column
定义列表来导出对象列表到 csv 文件中。
列定义包含列名和用于从该行的对象中提取该列值的 Function<T, X>
,X
取决于列类型 - 对于 StringColumn
预期函数是 Function<T, String>
,对于 DoubleColumn
预期函数是 Function<T, Double>
.
导出代码类似于
private void exportXxx(List<ExportableXxx> data) {
List<Column<ExportableXxx>> columns = Arrays.asList(
new StringColumn<>("Xxx Id", ExportableXxx::getId),
new StringColumn<>("Xxx Name", ExportableXxx::getName),
new DoubleColumn<>("Total YYYY", "%.2f", ExportableXxx::getTotal),
new DoubleColumn<>("Yyyy Type 1", "%.2f", e -> e.getYyyy(Types.Type1)),
new DoubleColumn<>("Yyyy Type 2", "%.2f", e -> e.getYyyy(Types.Type2)),
new DoubleColumn<>("Yyyy Type 3", "%.2f", e -> e.getYyyy(Types.Type3)),
new DoubleColumn<>("Yyyy Type 4", "%.2f", e -> e.getYyyy(Types.Type4)),
new DoubleColumn<>("Yyyy Other", "%.2f", ExportableXxx::getYyyOther)
);
try (ObjectCsvWriter<ExportableXxx> cw = new ObjectCsvWriter<>(
new PrintWriter(fileName, Charset.defaultCharset()), columns)) {
cw.printHeader();
for (ExportableXxx d: data) {
cw.printLine(d);
}
} catch (IOException e) {
logger.warn("Cannot write export file", e);
}
}
您能想象为列出的每个列创建匿名 class 需要付出的努力吗? (此示例不仅使用 lambda 来提取值,因为有时使用方法引用甚至更容易)。
我今天在学习 lambda 表达式,到目前为止,我还没有发现它们的独特用途。所以我问自己,它们是否不仅仅是一个取自函数式语言的方便的小工具。
什么是 Lambda 表达式?
根据我的阅读,它们可用于实例化匿名 类、传递简单方法和过滤集合中的元素。 例如:
List<Integer> list = List.of(1, 2, 3, 4, 5);
for (int i : list)
System.out.println(i);
...可以写成:
List<Integer> list = List.of(1, 2, 3, 4, 5);
list.forEach((i) -> {
System.out.println(i);
});
但是你为什么要那样做?每次你写下一个 lambda 表达式时,你可以只把原始表达式放在那里!
public class Test {
@FunctionalInterface
interface BoolOperation {
boolean execute(boolean param1, boolean param2);
}
static void test(BoolOperation op, boolean param1, boolean param2) {
System.out.println(op.execute(param1, param2));
}
public static void main(String[] args) {
test((x, y) -> x && y, true, false);
test((x, y) -> x || y, false, true);
test((x, y) -> x == y, true, true);
}
}
...与以下内容相同:
public class Test {
public static void main(String[] args) {
System.out.println(true && false);
System.out.println(false || true);
System.out.println(true == true);
}
}
和
public class Test {
public static void main(String[] args) {
Runnable r = () -> {
System.out.println("Running.");
};
}
}
... shorter/readable 并不比
public class Test {
public static void main(String[] args) {
Runnable r = new ThreadThingy();
}
}
class ThreadThingy implements Runnable {
public void run() {
System.out.println("Running.");
}
}
我读过 Oracle-Docs,但它们非常具体,没有突出一般用途。
当然,你的布尔表达式的例子直接写起来更容易。但是如果你想为不同的布尔表达式创建真值表呢?
public class Test {
@FunctionalInterface
interface BoolOperation {
boolean execute(boolean param1, boolean param2);
}
static void line(String s, BoolOperation op, boolean b1, boolean b2) {
System.out.printf("%b %s %b = %b%n", b1, s, b2, op.execute(b1, b2));
}
static void table(String s, BoolOperation op) {
System.out.println("Truth table for "+s);
line(s, op, false, false);
line(s, op, false, true);
line(s, op, true, false);
line(s, op, true, true);
System.out.println();
}
public static void main(String[] args) {
table("AND", (x, y) -> x && y);
table("OR", (x, y) -> x || y);
table("XNOR", (x, y) -> x == y);
table("XOR", (x, y) -> x != y);
table("implies", (x, y) -> !x || y);
}
}
对于各种操作,您仍然可以使用匿名 classes 来做到这一点,但是这将需要更多的“样板” - 需要编写的代码只是为了满足编译器而不增加任何价值。
例如,您可以这样编写第一个 table
调用:
table("AND", new BoolOperation() {
@Override
public boolean execute(boolean param1, boolean param2) {
return param1 && param2;
}
});
但你不能告诉我这比
更具可读性和更容易理解 table("AND", (x, y) -> x && y);
对于一个更“真实世界”的例子,我的一个项目包含一个 class ObjectCsvWriter
,它需要一个 PrintWriter
和一个 Column
定义列表来导出对象列表到 csv 文件中。
列定义包含列名和用于从该行的对象中提取该列值的 Function<T, X>
,X
取决于列类型 - 对于 StringColumn
预期函数是 Function<T, String>
,对于 DoubleColumn
预期函数是 Function<T, Double>
.
导出代码类似于
private void exportXxx(List<ExportableXxx> data) {
List<Column<ExportableXxx>> columns = Arrays.asList(
new StringColumn<>("Xxx Id", ExportableXxx::getId),
new StringColumn<>("Xxx Name", ExportableXxx::getName),
new DoubleColumn<>("Total YYYY", "%.2f", ExportableXxx::getTotal),
new DoubleColumn<>("Yyyy Type 1", "%.2f", e -> e.getYyyy(Types.Type1)),
new DoubleColumn<>("Yyyy Type 2", "%.2f", e -> e.getYyyy(Types.Type2)),
new DoubleColumn<>("Yyyy Type 3", "%.2f", e -> e.getYyyy(Types.Type3)),
new DoubleColumn<>("Yyyy Type 4", "%.2f", e -> e.getYyyy(Types.Type4)),
new DoubleColumn<>("Yyyy Other", "%.2f", ExportableXxx::getYyyOther)
);
try (ObjectCsvWriter<ExportableXxx> cw = new ObjectCsvWriter<>(
new PrintWriter(fileName, Charset.defaultCharset()), columns)) {
cw.printHeader();
for (ExportableXxx d: data) {
cw.printLine(d);
}
} catch (IOException e) {
logger.warn("Cannot write export file", e);
}
}
您能想象为列出的每个列创建匿名 class 需要付出的努力吗? (此示例不仅使用 lambda 来提取值,因为有时使用方法引用甚至更容易)。