Mar*_*ies 4 java lambda java-stream method-reference
我面临一个奇怪的问题
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Created by mklueh on 13/09/2022
*/
public class Reproducer {
private static final String COMMA_SEPARATED_STRING = "2020-05-09,hello ,10 ,11.345 ,true ,1000000000000";
/**
* Not working
*/
@Nested
public class WithReturn {
public String withReturn(Stream<String> stream, String delimiter, Function<? super String, String[]> tokenizer) {
return stream.map(s -> {
return Arrays.stream(tokenizer != null ? tokenizer.apply(s) : s.split(delimiter))
.map(String::strip) //Comment out to make it compile
.collect(Collectors.toList());
})
.flatMap(Collection::stream)
.collect(Collectors.joining(","));
}
@Test
void usingDelimiter() {
assertEquals(COMMA_SEPARATED_STRING.replaceAll(" ", ""),
withReturn(Stream.of(COMMA_SEPARATED_STRING), ",", null));
}
@Test
void usingTokenizer() {
assertEquals(COMMA_SEPARATED_STRING.replaceAll(" ", ""),
withReturn(Stream.of(COMMA_SEPARATED_STRING), null, s -> s.split(",")));
}
}
/**
* Working
*/
@Nested
public class WithReturnAndStripLambda {
public String withReturnAndStripLambda(Stream<String> stream, String delimiter, Function<? super String, String[]> tokenizer) {
return stream.map(s -> {
return Arrays.stream(tokenizer != null ? tokenizer.apply(s) : s.split(delimiter))
.map(s1 -> s1.strip())
.collect(Collectors.toList());
})
.flatMap(Collection::stream)
.collect(Collectors.joining(","));
}
@Test
void usingDelimiter() {
assertEquals(COMMA_SEPARATED_STRING.replaceAll(" ", ""),
withReturnAndStripLambda(Stream.of(COMMA_SEPARATED_STRING), ",", null));
}
@Test
void usingTokenizer() {
assertEquals(COMMA_SEPARATED_STRING.replaceAll(" ", ""),
withReturnAndStripLambda(Stream.of(COMMA_SEPARATED_STRING), null, s -> s.split(",")));
}
}
/**
* Working
*/
@Nested
public class WithMethodExpression {
public String withMethodExpression(Stream<String> stream, String delimiter, Function<? super String, String[]> tokenizer) {
return stream.map(s -> Arrays.stream(tokenizer != null ? tokenizer.apply(s) : s.split(delimiter))
.map(String::strip)
.collect(Collectors.toList())
)
.flatMap(Collection::stream)
.collect(Collectors.joining(","));
}
@Test
void usingDelimiter() {
assertEquals(COMMA_SEPARATED_STRING.replaceAll(" ", ""),
withMethodExpression(Stream.of(COMMA_SEPARATED_STRING), ",", null));
}
@Test
void usingTokenizer() {
assertEquals(COMMA_SEPARATED_STRING.replaceAll(" ", ""),
withMethodExpression(Stream.of(COMMA_SEPARATED_STRING), null, s -> s.split(",")));
}
}
}
Run Code Online (Sandbox Code Playgroud)
方法引用无效
error: incompatible types: invalid method reference
.map(String::strip)
^
method strip in class String cannot be applied to given types
required: no arguments
found: long
reason: actual and formal argument lists differ in length
Run Code Online (Sandbox Code Playgroud)
IntelliJ IDEA 甚至建议执行自动重构以摆脱 lambda 函数,转而使用方法引用
以及用表达式 lambda 替换 return
这里出了什么问题,为什么 IntelliJ 建议破坏代码/不认识到该建议会导致编译器错误?
这似乎很可能是 中的一个错误javac。IntelliJ 建议的应该是安全且功能等效的替代品。
看起来是由JDK-8268312引起的,它在 Java 20 中被标记为已修复。
如果不是,这是我在摆弄您的具体示例时发现的其他一些有趣的行为:
除了方法引用失败之外,当您在 lambda 参数列表中显式指定类型为 时,它也会失败String,即String::strip变为(String sa) -> sa.trim()
这是一个比你的更简单的例子。请注意,我故意使三元组的两条路径Arrays.stream执行完全相同的操作。我还使用了trim它,以便它可以在 Java 8 以及更高版本的 JDK 上运行。
Stream.of("A").flatMap(s -> {
return Arrays.stream(true ? s.split(",") : s.split(","))
.map((String sa) -> sa.trim());
})
.collect(Collectors.joining(","));
Run Code Online (Sandbox Code Playgroud)
出于某种原因,如果flatMap参数是表达式 lambda,而不是语句 lambda,则它可以正常编译,这不会对类型系统产生影响。
Stream.of("A").flatMap(s ->
Arrays.stream(true ? s.split(",") : s.split(","))
.map((String sa) -> sa.trim())
)
.collect(Collectors.joining(","));
Run Code Online (Sandbox Code Playgroud)
如果将三元的结果转换为 ,它也可以工作String[],即使这已经是该表达式的类型,这意味着转换应该是多余的。
Stream.of("A").flatMap(s -> {
return Arrays.stream((String[]) (true ? s.split(",") : s.split(",")))
.map((String sa) -> sa.trim());
})
.collect(Collectors.joining(","));
Run Code Online (Sandbox Code Playgroud)