Java 函数式编程
简略来说,函数式编程就是被注解@FunctionalInterface润饰的接口。
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface FunctionalInterface {}// 以jdk中的LongToIntFunction,相当于就是一个接口@FunctionalInterfacepublic interface LongToIntFunction { /** * Applies this function to the given argument. * * @param value the function argument * @return the function result */ int applyAsInt(long value);}// 这个接口能够被间接传入到函数中间接应用// 以jdk中的LongPipeline为例@Overridepublic final IntStream mapToInt(LongToIntFunction mapper) { Objects.requireNonNull(mapper); return new IntPipeline.StatelessOp<Long>(this, StreamShape.LONG_VALUE, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Long> opWrapSink(int flags, Sink<Integer> sink) { return new Sink.ChainedLong<Integer>(sink) { @Override public void accept(long t) { downstream.accept(mapper.applyAsInt(t)); } }; } };}
那么,函数是编程的interface怎么实现呢?由之前讲注解的时候咱们提到了,jdk内置的元注解,编译器是会辨认并给被该注解润饰的对象赋予编译器事后定义好的行为的。
然而,接口始终是接口,不领有理论的行为,理论的行为还是须要具体的实现类来定义,那么咱们来看看,函数式接口应该如何implement。以Springboot中的函数式接口TargetServerConnection为例
@FunctionalInterfacepublic interface TargetServerConnection { /** * Open a connection to the target server with the specified timeout. * @param timeout the read timeout * @return a {@link ByteChannel} providing read/write access to the server * @throws IOException in case of I/O errors */ ByteChannel open(int timeout) throws IOException;}// 实现类 SocketTargetServerConnection.java 中的open办法@Overridepublic ByteChannel open(int socketTimeout) throws IOException { SocketAddress address = new InetSocketAddress(this.portProvider.getPort()); logger.trace(LogMessage.format("Opening tunnel connection to target server on %s", address)); SocketChannel channel = SocketChannel.open(address); channel.socket().setSoTimeout(socketTimeout); return new TimeoutAwareChannel(channel);}// 在 HttpTunelServer中应用protected ServerThread getServerThread() throws IOException { synchronized (this) { if (this.serverThread == null) { ByteChannel channel = this.serverConnection.open(this.longPollTimeout); this.serverThread = new ServerThread(channel); this.serverThread.start(); } return this.serverThread; }}
能够看到,这种写法就是传统的接口实现办法,所以咱们讲,函数式接口就是一个接口,不是什么高级语法糖,@FunctionalInterface注解也并不会提供额定的性能,只是通过该注解润饰的接口,让编译器晓得,而后去断定该接口是否定义得合乎函数式接口的定义规定。
那么,到底函数式接口该如何应用呢?
要答复这个问题可不简略,先来看看上面的代码
@FunctionalInterfacepublic interface ExitCodeExceptionMapper { int getExitCode(Throwable exception);}
这个是springboot框架中定义获取退出码的函数式接口
// SpringApplicationTest.java@BeanExitCodeExceptionMapper exceptionMapper() { return (exception) -> { if (exception instanceof IllegalStateException) { return 11; } return 0; };}// SpringApplication.javaprivate int getExitCodeFromMappedException(ConfigurableApplicationContext context, Throwable exception) { if (context == null || !context.isActive()) { return 0; } ExitCodeGenerators generators = new ExitCodeGenerators(); Collection<ExitCodeExceptionMapper> beans = context.getBeansOfType(ExitCodeExceptionMapper.class).values(); generators.addAll(exception, beans); return generators.getExitCode();}
该函数式接口被应用的中央,从第一个例子咱们能够晓得,函数式接口所诠释的类型等价于其惟一办法的返回值所代表的类型。第二个例子通知咱们,函数式接口不肯定非要被实现能力取得理论行为,也能够通过反射失去。
源码剖析完结了,咱们来实现一个例子
// 定义一个函数式接口 Demo1.java@FunctionalInterfacepublic interface Demo1 { String getName(Integer code);}// 实现类 Demo2.javapublic class Demo2 implements Demo1{ @Override public String getName(Integer code) { StringBuilder builder = new StringBuilder(); return builder.append("my name is ").append(code).append("!").toString(); }}// 调用方 Demo3.javapublic class Demo3{ public static void main(String[] args){ // 在应用过程中实例化 Demo1 demo1 = new Demo1() { @Override public String getName(Integer code) { return new StringBuilder().append("my name is ").append(code).append("!").toString(); } }; System.out.println(demo1.getName(1)); System.out.println(demo1.getName(2)); // 一般接口 Demo1 demo2 = new Demo2(); System.out.println(demo2.getName(1)); System.out.println(demo2.getName(2)); // 简略的函数式编程 Demo1 demo3 = code -> { return new StringBuilder().append("my name is ").append(code).append("!").toString(); }; System.out.println(demo3.getName(1)); System.out.println(demo3.getName(2)); }}
下面三个打印后果是一样的,也就是说,对于函数式接口,咱们能够间接用这样的语法
Demo1 demo3 = code -> { return new StringBuilder().append("my name is ").append(code).append("!").toString(); };
来定义出其函数体。
带来的益处
简洁和稳固
炒鸡辣鸡原创文章,转载请注明起源