《On Java 8》中文版,又名《Java编程思想》 第5版
接下来的都是个人学习过程的笔记,不是总结,没有参考价值,但是这本书很棒
方法引用
未绑定方法
package chapter13;
class X{
String f(){
return "X::f()";
}
}
interface MakeString{
String make();
}
interface TransformX{
String transform(X x);
}
public class UnboundMethodReference {
public static void main(String[] args) {
TransformX sp=X::f;
// 简单说,就是告诉sp在调用transform的时候,当你接受了一个X对象的时候,调用其f方法
X x=new X();
MakeString ms=x::f;//也work
TransformX sp1=(a)->a.f();//do the same
System.out.println(sp.transform(x));
System.out.println(x.f());
}
}
刚开始没有理解上面这段代码的运作流程,实际上就是就是告诉sp在调用transform的时候,当你接受了一个X对象的时候,调用其f方法,上面代码里sp1
和sp
的生成过程一毛一样,这两个TransformX
的功能完全相同。这也就是说,X::f
会生成一个TransformX
类型的对象,这个对象的String transform(X x)
方法长成下面的这个样子
String transform(X x){
return x.f();
}
更进一步,看下面代码
class X{
String f(){
return "X::f()";
}
String f2(String s){
return s+"!";
}
}
interface MyTransformX{
String transform(X x,String s);
}
public class UnboundMethodReference {
public static void main(String[] args) {
MyTransformX mySp=X::f2;
System.out.println(mySp.transform(x,"hi"));
}
}
X::f2
会生成一个MyTransformX
类型的对象,这个对象的String transform(X x)
方法长成下面的这个样子
String transform(X x,String s){
return x.f2(s);
}
函数式接口
// functional/FunctionVariants.java
import java.util.function.*;
class Foo {}
class Bar {
Foo f;
Bar(Foo f) { this.f = f; }
}
class IBaz {
int i;
IBaz(int i) {
this.i = i;
}
}
class LBaz {
long l;
LBaz(long l) {
this.l = l;
}
}
class DBaz {
double d;
DBaz(double d) {
this.d = d;
}
}
public class FunctionVariants {
static Function<Foo,Bar> f1 = f -> new Bar(f);
static IntFunction<IBaz> f2 = i -> new IBaz(i);
static LongFunction<LBaz> f3 = l -> new LBaz(l);
static DoubleFunction<DBaz> f4 = d -> new DBaz(d);
static ToIntFunction<IBaz> f5 = ib -> ib.i;
static ToLongFunction<LBaz> f6 = lb -> lb.l;
static ToDoubleFunction<DBaz> f7 = db -> db.d;
static IntToLongFunction f8 = i -> i;
static IntToDoubleFunction f9 = i -> i;
static LongToIntFunction f10 = l -> (int)l;
static LongToDoubleFunction f11 = l -> l;
static DoubleToIntFunction f12 = d -> (int)d;
static DoubleToLongFunction f13 = d -> (long)d;
public static void main(String[] args) {
Bar b = f1.apply(new Foo());
IBaz ib = f2.apply(11);
LBaz lb = f3.apply(11);
DBaz db = f4.apply(11);
int i = f5.applyAsInt(ib);
long l = f6.applyAsLong(lb);
double d = f7.applyAsDouble(db);
l = f8.applyAsLong(12);
d = f9.applyAsDouble(12);
i = f10.applyAsInt(12);
d = f11.applyAsDouble(12);
i = f12.applyAsInt(13.0);
l = f13.applyAsLong(13.0);
}
}
上面的代码在构造的时候,其实可以用构造函数引用的,例如static Function<Foo,Bar> f1 = Bar::new;
package chapter13;
import java.util.function.*;
class In1 {
}
class In2 {
}
public class MethodConversion {
static void accept(In1 i1, In2 i2) {
System.out.println("accept()");
}
void boundAccept(In1 i1, In2 i2) {
System.out.println("boundAccept()");
}
static void someOtherName(In1 i1, In2 i2) {
System.out.println("someOtherName()");
}
public static void main(String[] args) {
BiConsumer<In1, In2> bic;
bic = MethodConversion::accept;
bic.accept(new In1(), new In2());
bic = MethodConversion::someOtherName;
bic.accept(new In1(), new In2());
MethodConversion m = new MethodConversion();
bic=m::boundAccept;
bic.accept(new In1(), new In2());
}
}
这段代码我增加了一点,注意main
函数里的第一个bic
,生成的时候用的是MethodConversion::accept
,但这个时候应该不算是非绑定引用吧,因为这个accept
是个静态方法,可以由类直接调用的。跟最后面那个m::boundAccept
其实差别不大。
高阶函数
package chapter13;
import java.util.function.*;
interface FuncSS extends Function<String,String>{}
public class ProduceFunction {
static FuncSS produce(){
return String::toUpperCase;
// return s->s.toUpperCase();
}
public static void main(String[] args) {
FuncSS f=produce();
System.out.println(f.apply("YELLING"));
}
}
第一段代码也可以直接用String::toUpperCase
的非绑定方法,因为Function
接口需要实现一个R apply(T t)
功能
// functional/TransformFunction.java
import java.util.function.*;
class I {
@Override
public String toString() { return "I"; }
}
class O {
@Override
public String toString() { return "O"; }
}
public class TransformFunction {
static Function<I,O> transform(Function<I,O> in) {
return in.andThen(o -> {
System.out.println(o);
return o;
});
}
public static void main(String[] args) {
Function<I,O> f2 = transform(i -> {
System.out.println(i);
return new O();
});
O o = f2.apply(new I());
}
}
andThen
是Function<T, R>
接口里的一个default
方法,具体实现如下,顾名思义,接受一个Function<? super R, ? extends V>
返回一个Function<T, V>
,当调用返回的对象的apply(T t)
方法的时候:先会执行Function<T, R>
的apply
返回一个R
类型对象,然后这个对象作为参数调用Function<? super R, ? extends V>
的apply(R r)
返回一个V
类型的对象。所以上面的代码就容易理解了,唯一需要说的就是,in.andThen
方法传入的是一个Function<O, O>
对象
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
闭包
从 Lambda 表达式引用的局部变量必须是 final 或者是等同 final 效果的。
这就叫做等同 final 效果(Effectively Final)。这个术语是在 Java 8 才开始出现的,表示虽然没有明确地声明变量是 final 的,但是因变量值没被改变过而实际有了 final 同等的效果。 如果局部变量的初始值永远不会改变,那么它实际上就是 final 的。
等同 final 效果意味着可以在变量声明前加上 final 关键字而不用更改任何其余代码。 实际上它就是具备 final 效果的,只是没有明确说明。
package chapter13;
import java.util.function.IntSupplier;
public class Closure1 {
int i;
IntSupplier makeFun(int x){
return ()->x+i++;
}
}
makeFun
被调用的时候,这个i
是会被jvm搜索的,这也就是说,jvm一直要持有一个i
的对象,书上是这么解释的
实际上,垃圾收集器几乎肯定会保留一个对象,并将现有的函数以这种方式绑定到该对象上
函数组合
// functional/FunctionComposition.java
import java.util.function.*;
public class FunctionComposition {
static Function<String, String>
f1 = s -> {
System.out.println(s);
return s.replace('A', '_');
},
f2 = s -> s.substring(3),
f3 = s -> s.toLowerCase(),
f4 = f1.compose(f2).andThen(f3);
public static void main(String[] args) {
System.out.println(
f4.apply("GO AFTER ALL AMBULANCES"));
}
}
上面的代码使用了compose
和andThen
,这两个方法的功能刚好相反
柯里化和部分求值
柯里化意为:将一个多参数的函数,转换为一系列单参数函数。