by @王爵nice
biezhi.me@gmail.com
你的时间非常重要,即便是坐在这里。
什么是 lmabda ?
public interface Comparator<T> {
int compare(T obj1, T obj2);
}
// Java 7
Collections.sort(peoples, new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name);
}
});
Java 7 的时代
public interface Comparator<T> {
int compare(T obj1, T obj2);
}
// Java 8
people.sort((p1, p2) -> p1.name.compareTo(p2.name));
// prefer
(p1, p2) -> p1.name.compareTo(p2.name);
// avoid
(Person p1, Person p2) -> p1.name.compareTo(p2.name);
// prefer
str -> str.toUpperCase(Locale.US);
// avoid
(str) -> str.toUpperCase(Locale.US);
当小括号可选的时候不要写小括号
public UnaryOperator<String> upperCaser(Locale locale) {
return str -> str.toUpperCase(locale);
}
这里无需声明 final
// prefer
str -> str.toUpperCase(Locale.US);
// use with care
str -> {
return str.toUpperCase(Locale.US);
}
private int doFoo() {
// 很长的一段代码
// foo 的核心逻辑
// 又是很长一串代码
}
private int doBar() {
// 很长的一段代码
// bar 的核心逻辑
// 又是很长一串代码
}
private int doFoo() {
return doFooBar( lambdaOfFooSpecificLogic );
}
private int doBar() {
return doFooBar( lambdaOfBarSpecificLogic );
}
private int doFooBar(Function<A, B> fn) {
// 很长一段代码
result = fn.apply(arg);
// 很长一段代码
}
@FunctionalInterface
public interface FooBarQuery {
public abstract Foo findAllFoos(Bar bar);
}
private String nameGreet(Supplier<String> nameSupplier) {
return "Hello " + nameSupplier.get();
}
// 使用 lambda 调用
String greeting = nameGreet(() -> "空中金融");
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:
// avoid
public class Foo<T> {
public Foo<R> apply(Function<T, R> fn);
public Foo<T> apply(UnaryOperator<T> fn);
}
使用不同的方法名称以避免冲突
// prefer
public class Foo<T> {
public Foo<R> applyFunction(Function<T, R> fn);
public Foo<T> applyOperator(UnaryOperator<T> fn);
}
// prefer
public Foo parse(Locale locale, Function<Locale,Foo> fn);
// avoid
public Foo parse(Function<Locale,Foo> fn, Locale locale);
// 编译不通过
public Function<String, Class> loader() {
return className -> Class.forName(className);
}
public Function<String, Class> loader() {
return Unchecked.function(Class::forName);
}
说起空指针你们会想到什么?
服务器又炸了
没错,是的!
String a = "AB";
String b = null;
Optional<String> a = Optional.of("AB");
Optional<String> b = Optional.empty();
Optional 有什么需要注意的 ?
String a = "AB";
String b = null;
Optional<String> a = Optional.of("AB");
Optional<String> b = Optional.empty();
Optional<String> c = null; // NO NO NO
// 当某个库、API 没找到会返回 NULL
public Foo findFoo(String key) { … }
// 程序中必须记住要对那些可能为 NULL 的变量做检查
Foo foo = findFoo(key);
if (foo == null) {
foo = Foo.DEFAULT; // 或者抛出一个异常
}
// 当某个库、API 没找到会返回 Optional
public Optional<Foo> findFoo(String key) { … }
// 获取值的代码
Foo foo = findFoo(key).orElse(Foo.DEFAULT);
// 或者
Foo foo = findFoo(key).orElseThrow(RuntimeException::new);
← 以往判断 NULL 的方式
Optional 的方式 →
// prefer
Foo foo = findFoo(key).orElse(Foo.DEFAULT);
// avoid ifPresent()
Optional<Foo> optFoo = findFoo(key);
if (optFoo.ifPresent()) { … }
你会选什么 ?
A. 在哪里都使用 Optional
B. 在公共 API、入参和返回值上使用而不是NULL
C. 在公共返回类型上使用而不是 NULL
D. 在几个选定的地方使用
E. 不用
存在即返回,否则提供默认值
//而不是 return user.isPresent() ? user.get() : null;
return user.orElse(null);
return user.orElse(UNKNOWN_USER);
存在即返回, 无则由函数来产生
return user.orElseGet(() -> fetchAUserFromDatabase());
//而不要 return user.isPresent() ? user: fetchAUserFromDatabase();
user.ifPresent(System.out::println);
//而不要下边那样
if (user.isPresent()) {
System.out.println(user.get());
}
存在才对它做点儿什么
使用 map 转换结果
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
// 上面避免了我们类似 Java 8 之前的做法
if(user.isPresent()) {
return user.get().getOrders();
} else {
return Collections.emptyList();
}
return user.map(u -> u.getUsername())
.map(name -> name.toUpperCase())
.orElse(null);
map 是无限层级的,可以在深一层获取大写用户名
List<Trade> trades = loadTrades();
List<Money> valued = new ArrayList<Money>();
for (Trade t : trades) {
if (t.isActive()) {
Money pv = presentValue(t);
valued.add(pv);
}
}
List<Trade> trades = loadTrades();
List<Money> valued =
trades.stream()
.filter(t -> t.isActive())
.map(t -> presentValue(t))
.collect(Collectors.toList());
BEFORE
AFTER
// avoid
strings.stream().filter(s -> s.length() > 2).sorted()
.map(s -> s.substring(0, 2)).collect(Collectors.toList());
// prefer
strings.stream()
.filter(s -> s.length() > 2)
.sorted()
.map(s -> s.substring(0, 2))
.collect(Collectors.toList());
每行最多有一个流方法调用。
像 map、filter、sorted 这样的操作更容易识别
// avoid
strings.stream()
.sorted(Comparator.reverseOrder())
.limit(10)
.collect(Collectors.toMap(Function.identity(), String::length));
// prefer
strings.stream()
.sorted(reverseOrder())
.limit(10)
.collect(toMap(identity(), String::length));
导入 Stream API 中的所有静态方法,减少视觉干扰。
这将使你的代码变得更加简洁、容易阅读和理解。
// avoid
strings.stream()
.map(s -> s.length())
.collect(toList());
// prefer
strings.stream()
.map(String::length)
.collect(toList());
方法引用更容易阅读,因为我们避免了 -> 和 () 操作符产生的所有视觉干扰。像 s -> s.length() 这样的 lambda 表达式被编译为一个私有静态方法和一个 invokedynamic 指令。
// s -> s.lenght() 被翻译为下面的代码
private static Integer lambda$main$0(String s) {
return s.length();
}
Stream<Object> objects = Stream.of(
"a string",
42,
new String[] { "an array" },
"another string");
List<String> strings = objects
.filter(String.class::isInstance)
.map(String.class::cast)
.collect(toList());
如何获取该流中的所有字符串类型,收集为一个 List ?
// 转换 Entity 为 Map
Map<Integer, Entity> entityById = entities.stream()
.collect(toMap(Entity::getId, identity()));
// 经常使用的情况
Map<Integer, Entity> entityById = entities.stream()
.collect(ExtraCollectors.toByIdMap());
private static class ExtraCollectors {
public static Collector<Entity,?,Map<Integer,Entity>> toByIdMap() {
return Collectors.toMap(Entity::getId, identity());
}
}
为经常使用的收集器起一个有意义的名称,
同时可以将收集器提取为一个方法。
public List<Money> value(List<Trade> trades, Data data) {
return trades.stream()
.filter(t -> t.isActive(data))
.map(valueFn)
.collect(Collectors.toList());
}
List<Trade> trades = loadTrades();
Predicate<Trade> activePredicate = t -> t.isActive();
Function<Trade, Money> valueFn = t -> presentValue(t);
List<Money> valued =
trades.stream()
.filter(activePredicate)
.map(valueFn)
.collect(Collectors.toList());
lambda 中的常见问题
如果难以编译,就把他们提取出来
java.lang.IllegalArgumentException: Oops
at com.opengamma.strata.calc.DefaultCalculationRunner.lambda$2(DefaultCalculationRunner.java:98)
at java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:372)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at com.opengamma.strata.calc.DefaultCalculationRunner.calculate(DefaultCalculationRunner.java:100)
at com.opengamma.strata.calc.DefaultCalculationRunner.lambda$0(DefaultCalculationRunner.java:86)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at com.opengamma.strata.calc.DefaultCalculationRunner.calculate(DefaultCalculationRunner.java:87)
at com.opengamma.strata.calc.DefaultCalculationRunnerTest.calculate(DefaultCalculationRunnerTest.java:49)
java.lang.IllegalArgumentException: Oops
at com.opengamma.strata.calc.DefaultCalculationRunner.calculate(DefaultCalculationRunner.java:102)
at com.opengamma.strata.calc.DefaultCalculationRunner.calculate(DefaultCalculationRunner.java:87)
at com.opengamma.strata.calc.DefaultCalculationRunnerTest.calculate(DefaultCalculationRunnerTest.java:49)
// prefer
LocalDate date = LocalDate.of(2018, 05, 10);
// avoid
Temporal date = LocalDate.of(2018, 05, 10);