尝试在 Java 中使用 lambda 进行较小的重构
Trying to do a minor refactor using lambdas in Java
我正在编写一个程序的一部分(关于语音识别和遥控车),其中代码 transmit(XXXXX); disableAutoMode();
重复了很多次。出于好奇,我想将其转换为类似于 var f = p -> transmit(p); disableAutoMode();
的 lambda 函数(请原谅 var
;我不知道表达式的类型是什么),然后在与此类似的方式:f("s");
、f("a");
和 f("f");
或类似于 f.call("s");
、f.call("a");
和 f.call("f");
.
的方式
在 Java 中使用类似于我上面描述的简单 lambda 函数的正确语法是什么? (我应该写什么而不是说 var
?)
这是代码块,如果你好奇的话:
@Override
public void onResult(Hypothesis hypothesis) {
if (hypothesis != null) {
String text = hypothesis.getHypstr();
Log.i("onresult",text);
ToastMaster(text);
switch (text) {
case "forward":
case "go forward":
transmit("f");
disableAutoMode();
break;
case "go back":
case "go backward":
case "back":
case "backward":
case "reverse":
transmit("b");
disableAutoMode();
break;
case "skid left":
case "go left":
transmit("l");
disableAutoMode();
break;
case "skid right":
case "go right":
transmit("r");
disableAutoMode();
break;
case "turn left":
transmit("q");
disableAutoMode();
break;
case "turn right":
transmit("e");
disableAutoMode();
break;
case "reverse left":
transmit("z");
disableAutoMode();
break;
case "reverse right":
transmit("x");
disableAutoMode();
break;
case "stop":
disableAutoMode();
break;
case "automatic":
toggleAutoMode(null);
break;
case "enable automatic mode":
enableAutoMode();
break;
case "disable automatic mode":
disableAutoMode();
break;
}
}
}
调用 lambda 会比使用简单的辅助方法更乏味:
private void m(String x) {
transmit(x);
disableAutoMode();
}
一个(更详细的)替代方法是使用 lambda:
Consumer<String> consumer = (x) -> {
transmit(x);
disableAutoMode();
};
然后调用它类似于
consumer.accept("f");
在这种情况下,您需要 Consumer
。
Consumer<String> function = (x) -> { transmit(x); disableAutoMode(); };
function.accept("hello!");
但是我不确定你为什么要在这里使用 lambda 表达式,你可以创建一个普通的旧方法并调用它。
如果您希望进行更有意义的重构,一种选择是切换到 String
、Action/Runnable
的映射。虽然您最终会得到更多代码,但重构的目标不是“使其更小”,而是使其更多readable/maintainable。通过将每个动作拆分成自己的小独立 class,每个动作都可以用最少的设置进行隔离测试。每个动作都可以重复使用(因为它只是一个 class)。有了好的命名策略,读者就可以一目了然,而无需深入研究大量的 switch 语句。
你想做的,用lambda表达式确实可以,但节省的不会那么多:
@Override
public void onResult(Hypothesis hypothesis) {
if (hypothesis != null) {
String text = hypothesis.getHypstr();
Log.i("onresult",text);
ToastMaster(text);
Consumer<String> transmitDisable=s->{ transmit(s); disableAutoMode(); };
switch (text) {
case "forward": case "go forward":
transmitDisable.accept("f");
break;
case "go back": case "go backward": case "back":
case "backward": case "reverse":
transmitDisable.accept("b");
break;
case "skid left": case "go left":
transmitDisable.accept("l");
break;
case "skid right": case "go right":
transmitDisable.accept("r");
break;
case "turn left": transmitDisable.accept("q"); break;
case "turn right": transmitDisable.accept("e"); break;
case "reverse left": transmitDisable.accept("z"); break;
case "reverse right": transmitDisable.accept("x"); break;
case "stop": disableAutoMode(); break;
case "automatic": toggleAutoMode(null); break;
case "enable automatic mode": enableAutoMode(); break;
case "disable automatic mode": disableAutoMode(); break;
}
}
}
但是如果您考虑 Java:
的不常用代码流控制结构,您也可以删除没有 lambda 表达式的代码重复
@Override
public void onResult(Hypothesis hypothesis) {
if (hypothesis != null) {
String text = hypothesis.getHypstr();
Log.i("onresult",text);
ToastMaster(text);
transmitAndDisable: {
final String toTransmit;
switch (text) {
case "forward": case "go forward": toTransmit="f"; break;
case "go back": case "go backward": case "back":
case "backward": case "reverse": toTransmit="b"; break;
case "skid left": case "go left": toTransmit="l"; break;
case "skid right": case "go right": toTransmit="r"; break;
case "turn left": toTransmit="q"; break;
case "turn right": toTransmit="e"; break;
case "reverse left": toTransmit="z"; break;
case "reverse right": toTransmit="x"; break;
case "stop": disableAutoMode(); break transmitAndDisable;
case "automatic": toggleAutoMode(null); break transmitAndDisable;
case "enable automatic mode": enableAutoMode(); break transmitAndDisable;
case "disable automatic mode": disableAutoMode(); break transmitAndDisable;
default: break transmitAndDisable;
}
transmit(toTransmit);
disableAutoMode();
}
}
}
结构可能不太容易阅读,但请注意将 toTransmit
声明为 final
有很大帮助。因为它是 final
,它不能用回退值初始化,这意味着每个进入共享 transmit(…); disableAutoMode();
代码的替代方案必须恰好初始化变量一次。
换句话说,
- 如果你忘记在一个不打算调用共享代码的case的
break
处指定transmitAndDisable
标签,编译器会立即大喊大叫,因为toTransmit
没有已初始化。
- 如果您忘记了要调用共享代码的备选方案之间的中断,编译器将立即拒绝该代码,因为它分配了两次
toTransmit
- 如果您在要调用共享代码的案例的
break
处错误地指定了 transmitAndDisable
标签,好的编译器会发出有关未使用赋值的警告
更雄心勃勃的重构将建立在 lambda 所涉及的 "turn code into data" 原则之上,同时将您的 switch 语句从代码转换为数据。怎么样:
// One-time setup of the machine
Map<String, Consumer<String>> actions = new HashMap<>();
actions.put("go forward", x -> { transmit(x); disableAutoMode(); });
actions.put(...)
...
public void onResult(Hypothesis hypothesis) {
if (hypothesis != null) {
String text = hypothesis.getHypstr();
Log.i("onresult",text);
ToastMaster(text);
Consumer<String> action = actions.get(text);
if (action != null)
action.accept(text);
}
}
我正在编写一个程序的一部分(关于语音识别和遥控车),其中代码 transmit(XXXXX); disableAutoMode();
重复了很多次。出于好奇,我想将其转换为类似于 var f = p -> transmit(p); disableAutoMode();
的 lambda 函数(请原谅 var
;我不知道表达式的类型是什么),然后在与此类似的方式:f("s");
、f("a");
和 f("f");
或类似于 f.call("s");
、f.call("a");
和 f.call("f");
.
在 Java 中使用类似于我上面描述的简单 lambda 函数的正确语法是什么? (我应该写什么而不是说 var
?)
这是代码块,如果你好奇的话:
@Override
public void onResult(Hypothesis hypothesis) {
if (hypothesis != null) {
String text = hypothesis.getHypstr();
Log.i("onresult",text);
ToastMaster(text);
switch (text) {
case "forward":
case "go forward":
transmit("f");
disableAutoMode();
break;
case "go back":
case "go backward":
case "back":
case "backward":
case "reverse":
transmit("b");
disableAutoMode();
break;
case "skid left":
case "go left":
transmit("l");
disableAutoMode();
break;
case "skid right":
case "go right":
transmit("r");
disableAutoMode();
break;
case "turn left":
transmit("q");
disableAutoMode();
break;
case "turn right":
transmit("e");
disableAutoMode();
break;
case "reverse left":
transmit("z");
disableAutoMode();
break;
case "reverse right":
transmit("x");
disableAutoMode();
break;
case "stop":
disableAutoMode();
break;
case "automatic":
toggleAutoMode(null);
break;
case "enable automatic mode":
enableAutoMode();
break;
case "disable automatic mode":
disableAutoMode();
break;
}
}
}
调用 lambda 会比使用简单的辅助方法更乏味:
private void m(String x) {
transmit(x);
disableAutoMode();
}
一个(更详细的)替代方法是使用 lambda:
Consumer<String> consumer = (x) -> {
transmit(x);
disableAutoMode();
};
然后调用它类似于
consumer.accept("f");
在这种情况下,您需要 Consumer
。
Consumer<String> function = (x) -> { transmit(x); disableAutoMode(); };
function.accept("hello!");
但是我不确定你为什么要在这里使用 lambda 表达式,你可以创建一个普通的旧方法并调用它。
如果您希望进行更有意义的重构,一种选择是切换到 String
、Action/Runnable
的映射。虽然您最终会得到更多代码,但重构的目标不是“使其更小”,而是使其更多readable/maintainable。通过将每个动作拆分成自己的小独立 class,每个动作都可以用最少的设置进行隔离测试。每个动作都可以重复使用(因为它只是一个 class)。有了好的命名策略,读者就可以一目了然,而无需深入研究大量的 switch 语句。
你想做的,用lambda表达式确实可以,但节省的不会那么多:
@Override
public void onResult(Hypothesis hypothesis) {
if (hypothesis != null) {
String text = hypothesis.getHypstr();
Log.i("onresult",text);
ToastMaster(text);
Consumer<String> transmitDisable=s->{ transmit(s); disableAutoMode(); };
switch (text) {
case "forward": case "go forward":
transmitDisable.accept("f");
break;
case "go back": case "go backward": case "back":
case "backward": case "reverse":
transmitDisable.accept("b");
break;
case "skid left": case "go left":
transmitDisable.accept("l");
break;
case "skid right": case "go right":
transmitDisable.accept("r");
break;
case "turn left": transmitDisable.accept("q"); break;
case "turn right": transmitDisable.accept("e"); break;
case "reverse left": transmitDisable.accept("z"); break;
case "reverse right": transmitDisable.accept("x"); break;
case "stop": disableAutoMode(); break;
case "automatic": toggleAutoMode(null); break;
case "enable automatic mode": enableAutoMode(); break;
case "disable automatic mode": disableAutoMode(); break;
}
}
}
但是如果您考虑 Java:
的不常用代码流控制结构,您也可以删除没有 lambda 表达式的代码重复@Override
public void onResult(Hypothesis hypothesis) {
if (hypothesis != null) {
String text = hypothesis.getHypstr();
Log.i("onresult",text);
ToastMaster(text);
transmitAndDisable: {
final String toTransmit;
switch (text) {
case "forward": case "go forward": toTransmit="f"; break;
case "go back": case "go backward": case "back":
case "backward": case "reverse": toTransmit="b"; break;
case "skid left": case "go left": toTransmit="l"; break;
case "skid right": case "go right": toTransmit="r"; break;
case "turn left": toTransmit="q"; break;
case "turn right": toTransmit="e"; break;
case "reverse left": toTransmit="z"; break;
case "reverse right": toTransmit="x"; break;
case "stop": disableAutoMode(); break transmitAndDisable;
case "automatic": toggleAutoMode(null); break transmitAndDisable;
case "enable automatic mode": enableAutoMode(); break transmitAndDisable;
case "disable automatic mode": disableAutoMode(); break transmitAndDisable;
default: break transmitAndDisable;
}
transmit(toTransmit);
disableAutoMode();
}
}
}
结构可能不太容易阅读,但请注意将 toTransmit
声明为 final
有很大帮助。因为它是 final
,它不能用回退值初始化,这意味着每个进入共享 transmit(…); disableAutoMode();
代码的替代方案必须恰好初始化变量一次。
换句话说,
- 如果你忘记在一个不打算调用共享代码的case的
break
处指定transmitAndDisable
标签,编译器会立即大喊大叫,因为toTransmit
没有已初始化。 - 如果您忘记了要调用共享代码的备选方案之间的中断,编译器将立即拒绝该代码,因为它分配了两次
toTransmit
- 如果您在要调用共享代码的案例的
break
处错误地指定了transmitAndDisable
标签,好的编译器会发出有关未使用赋值的警告
更雄心勃勃的重构将建立在 lambda 所涉及的 "turn code into data" 原则之上,同时将您的 switch 语句从代码转换为数据。怎么样:
// One-time setup of the machine
Map<String, Consumer<String>> actions = new HashMap<>();
actions.put("go forward", x -> { transmit(x); disableAutoMode(); });
actions.put(...)
...
public void onResult(Hypothesis hypothesis) {
if (hypothesis != null) {
String text = hypothesis.getHypstr();
Log.i("onresult",text);
ToastMaster(text);
Consumer<String> action = actions.get(text);
if (action != null)
action.accept(text);
}
}