乐趣区

关于node.js:从源码角度学习java函数式编程

Java 函数式编程

简略来说,函数式编程就是被注解 @FunctionalInterface 润饰的接口。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

// 以 jdk 中的 LongToIntFunction,相当于就是一个接口
@FunctionalInterface
public interface LongToIntFunction {
    /**
     * Applies this function to the given argument.
     *
     * @param value the function argument
     * @return the function result
     */
    int applyAsInt(long value);
}

// 这个接口能够被间接传入到函数中间接应用
// 以 jdk 中的 LongPipeline 为例
@Override
public 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 为例

@FunctionalInterface
public 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 办法

@Override
public 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 注解也并不会提供额定的性能,只是通过该注解润饰的接口,让编译器晓得,而后去断定该接口是否定义得合乎函数式接口的定义规定。

那么,到底函数式接口该如何应用呢?

要答复这个问题可不简略,先来看看上面的代码

@FunctionalInterface
public interface ExitCodeExceptionMapper {int getExitCode(Throwable exception);
}

这个是 springboot 框架中定义获取退出码的函数式接口

// SpringApplicationTest.java
@Bean
ExitCodeExceptionMapper exceptionMapper() {return (exception) -> {if (exception instanceof IllegalStateException) {return 11;}
        return 0;
    };
}

// SpringApplication.java
private 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
@FunctionalInterface
public interface Demo1 {String getName(Integer code);
}

// 实现类 Demo2.java
public 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.java
public 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();};

来定义出其函数体。

带来的益处

简洁和稳固

炒鸡辣鸡原创文章,转载请注明起源

退出移动版