随着 Java8
的发布,大家翘首以待的 FP
(函数式编程,后文皆以FP简称)终于面世。其实早在 1.7
这个版本就已经准备发布,但是由于还属于 Sun
刚被收购的磨合期所耽误。而 Java8
这个版本也一再延误才终于发布。
早些时候说起 Java
,大家对他的第一印象就是冗长,虽然我们可以通过 IDEA
等工具帮我们解决这些问题,但是可读性差的问题仍无法避免。
于是, lambda
和函数式编程呼之欲出。
在 Java
中函数是表达式与 lambda
密不可分,而说到 lambda
表达式,又就不得不提及 @FunctionInterface
这个注解,当你点开这个注解,你会发现很多你熟悉的类(Comparator、Runnable等)都使用了这个注解,而这个注解也是 Java
实现函数式编程编程中尤为重要的一环。
在解释这个注解实现原理之前,我们不妨先使用一下 lambda
看看其效果。
//Java8之前 Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); //Java8之后 Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getName())); //或者你可以使用方法引用 Thread thread1 = new Thread(StreamDemo::sayHello); private static void sayHello() { System.out.println("hello,world"); }
如果是你,你会如何设计这套语法糖的实现呢?
万变不离其宗, lambda
只是语法糖,虽然我们使用 lambda
表达式简化了我们的代码,但是并不意味着我们不需要实现 Runnable
,我们不妨大胆猜测一下:是不是因为 @FunctionalInterface
注解自动帮我们生成了一个 Runnable
的实现?
没错, Java
会在编译时帮我们动态生成一个对象来实现 Runnable
,我们可以通过参数 -Djdk.internal.lambda.dumpProxyClasses
来帮助我们把生成的类输出。
java -classpath your-class-path -Djdk.internal.lambda.dumpProxyClasses com.nineyang.LambdaDemo
通过执行上述代码,我们会发现生成了两个名字比较奇特的类:
//LambdaDemo$$Lambda$1.class final class LambdaDemo$$Lambda$1 implements Runnable { private LambdaDemo$$Lambda$1() { } @Hidden public void run() { LambdaDemo.lambda$main$0(); } } //LambdaDemo$$Lambda$2.class final class LambdaDemo$$Lambda$2 implements Runnable { private LambdaDemo$$Lambda$2() { } @Hidden public void run() { LambdaDemo.sayHello(); } }
此时又有一个奇奇怪怪的方法出现了, LambdaDemo.sayHello()
我们可以理解,但是 LambdaDemo.lambda$main$0()
是什么鬼?我们可重来没有写过这个方法啊。
我们不妨再猜测一下,是不是因为生成实例的同时,也给我们生成了一个方法呢?既然这个静态方法在我们所写的类中,那我们不妨看看这个类生成的字节码:
//命令行执行 javap -p -c target.classes.com.nineyang.LambdaDemo
//截取其中一部分字节码 private static void lambda$main$0(); Code: 0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 3: invokestatic #10 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread; 6: invokevirtual #11 // Method java/lang/Thread.getName:()Ljava/lang/String; 9: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 12: return
我想看到这里,已经不需要我再过多解释,大家也能明白其所做的事情是什么了。
不过,也许此时在你心中有了一个疑惑,为什么这些名字这么奇怪?在哪里可以看到生成的规则呢?如果我自定义一个 FunctionInterface
结果会怎样呢?
public class LambdaDemo2 { public static void main(String[] args) { LambdaPrintFunction lambdaPrintFunction = System.out::println; lambdaPrintFunction.print("hello,nine"); } } @FunctionalInterface interface LambdaPrintFunction { void print(String x); }
//生成的实现类 final class LambdaDemo2$$Lambda$1 implements LambdaPrintFunction { private final PrintStream arg$1; private LambdaDemo2$$Lambda$1(PrintStream var1) { this.arg$1 = var1; } private static LambdaPrintFunction get$Lambda(PrintStream var0) { return new LambdaDemo2$$Lambda$1(var0); } @Hidden public void print(String var1) { this.arg$1.println(var1); } }
此时生成的情况会变得稍显复杂,那么生成的规则到底在哪可以看呢?我们不妨带上附加信息后再看看我们的字节码
javap -c -p -v target.classes.com.nineyang.LambdaDemo2
SourceFile: "LambdaDemo2.java" InnerClasses: public static final #56= #55 of #61; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles BootstrapMethods: 0: #29 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method arguments: #30 (Ljava/lang/String;)V #31 invokevirtual java/io/PrintStream.println:(Ljava/lang/String;)V #30 (Ljava/lang/String;)V
我们拿到最后的附加信息之后,发现又一个非常有用的提示: invokestatic java/lang/invoke/LambdaMetafactory.metafactory
//LambdaMetafactory.metafactory方法 public static CallSite metafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType) throws LambdaConversionException { AbstractValidatingLambdaMetafactory mf; mf = new InnerClassLambdaMetafactory(caller, invokedType, invokedName, samMethodType, implMethod, instantiatedMethodType, false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY); mf.validateMetafactoryArgs(); return mf.buildCallSite(); }
该方法会帮助调度 InnerClassLambdaMetafactory
来我们生成实现类,而 InnerClassLambdaMetafactory
中,就有了我们所需要的参数相关的生成规则。
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, MethodType invokedType, String samMethodName, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType, boolean isSerializable, Class<?>[] markerInterfaces, MethodType[] additionalBridges) throws LambdaConversionException { super(caller, invokedType, samMethodName, samMethodType, implMethod, instantiatedMethodType, isSerializable, markerInterfaces, additionalBridges); implMethodClassName = implDefiningClass.getName().replace('.', '/'); implMethodName = implInfo.getName(); implMethodDesc = implMethodType.toMethodDescriptorString(); implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial) ? implDefiningClass : implMethodType.returnType(); constructorType = invokedType.changeReturnType(Void.TYPE); lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet(); cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); int parameterCount = invokedType.parameterCount(); if (parameterCount > 0) { argNames = new String[parameterCount]; argDescs = new String[parameterCount]; for (int i = 0; i < parameterCount; i++) { argNames[i] = "arg$" + (i + 1); argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i)); } } else { argNames = argDescs = EMPTY_STRING_ARRAY; } }
好了,这就是 lambda
表达式的基本原理。前面我们用到了自定义的函数式编程,不过坦率地讲,我们在实际工作中用的非常少,因为 Java
已经帮我们定义好了我们可能会用到的一些接口。
@FunctionalInterface public interface Consumer<T> { void accept(T t); default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
Consumer
是对数据源的操作,没有返回值,同时提供了 andThen
方法来帮助我们链式调用。这里需要说明的是,注解 @FunctionalInterface
是可以有默认方法和静态方法的,但是, 接口实现只能有一个 。
public class ConsumerDemo { public static void main(String[] args) { Consumer<String> consumer = (name) -> System.out.println("hello," + name); consumer.accept("nineyang"); } }
@FunctionalInterface public interface Predicate<T> { boolean test(T t); default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate<T> negate() { return (t) -> !test(t); } default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
Predicate
,顾名思义,用于判定表达式执行的结果,返回值是 boolean
。
public class PredicateDemo { public static void main(String[] args) { Predicate<Integer> predicate = (x) -> x > 0; boolean result = predicate.test(10); } }
@FunctionalInterface public interface Function<T, R> { R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity() { return t -> t; } }
Function
,可以执行一段逻辑,比如用于转化类型,将一种类型转换为另外一种。
public class FunctionDemo { public static void main(String[] args) { Function<String,Integer> function = Integer::valueOf; Integer result = function.apply("10"); } }
@FunctionalInterface public interface Supplier<T> { T get(); }
Supplier
,用于获取结果。
public class SupplierDemo { public static void main(String[] args) { Supplier<String> supplier = () -> "hello,nineyang"; System.out.println(supplier.get()); } }
这就是 Java
中最常用的四大函数式编程的接口了,其他的也基本上是围绕这四种来展开的,这也与我们接下来关于 Stream
的篇幅内容息息相关,但是由于篇幅有限,这里便不再赘述。
本文由nine 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Apr 17, 2020 at 11:54 pm