51

java8新特性function和lambda深度解析

 5 years ago
source link: https://blog.shareworld.vip/article/43?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

继续java8新亮点的源码之路,functional interface是一个跳不过的坎,它与lambda的结合使用非常普遍。 java.util.function 包对于每一个java工程师来说是必备技能,也是最基础的能力,一定要掌握。

J3aQfay.jpg!web

函数编程的最直接的表现在于将函数作为数据自由传递,结合泛型推导能力使代码表达能力获得飞一般的提升。同时Lambda表达式让你能够将函数作为方法参数或者将代码作为数据对待,让你发现“行级代码”优美。

java8引入新的注解,@FunctionalInterface

函数式注解@FunctionalInterface添加在一个接口上,主要是编译器检查提示作用。

  1. 注解的作用是检测自定义functional接口是否符合要求,编译器会有错误提示;
  2. 一个接口符合functional的要求,不加这个注解也可以正常使用, 建议都加上
  3. 有且只能有一个抽象方法但可以有多个非抽象方法,简单说就是接口里面default和static的方法是可以有多个的,其他的方法只能有一个。

lambda表达式写法及注意点

格式: ( parameters ) -> { statements; }

  1. 不需要声明参数类型,编译器可以识别参数值;
  2. 单个参数和语句下,圆括弧和大括弧可以省略;
  3. 表达式是一个闭包,定义了行内执行的方法类型接口;
  4. 只能引用标记了final的外层局部变量,不能在表达式内部修改定义在域外的局部变量,否则会编译错误;
  5. 表达式当中不允许声明一个与局部变量同名的参数或者局部变量;

写法示例:

@FunctionalInterface
public interface IPerson {

    String say(String input);
    //void stand(); 只能有一个抽象方法,不然编译无法默认识别调用

    static void run(String xx){
        PrintUtil.printTest("IPerson run : " + xx);
    }

    static void walk(){
        PrintUtil.printTest("IPerson walk");
    }

    default void eat(int a, int b){
        PrintUtil.printTest("IPerson eat : " + a + " - " + b);
    }
}
//当你这种写法是编译器会提示你用lambda
IPerson person = new IPerson() {
    @Override
    public String say(String input) {
        return "My said is " + input;
    }
};
PrintUtil.printTest(person.say("i love china."));

//lambda写法
IPerson person2 = a -> "My said is " +a;
PrintUtil.printTest(person2.say("i love china."));
//结果是一样的,My said is i love china.

function包中重要接口源码分析

fIFVRna.jpg!web

Consumer,接收一个输入参数T类型并不没有返回值;andThen看源码可以知道是添加一个其后执行的Consumer对象。

这个接口很简单不需要什么解释,看源码一眼OK。

@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); };
    }
}

Function,接收一个T类型参数,返回一个R类型的结果。需要注意的是compose\andThen的传入参数和范围参数规则不同,这里的参数类型稍有不慎就会出错,复杂的链路里面排查bug是非常麻烦的事。

@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);
	//生成了function的参数类型同before一样
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
	//新生成的function的返回值类型要after一样
    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<Integer,String> function = a -> "== " + a;
PrintUtil.printTest(function.apply(101));
Function<String,Boolean> function1 = c -> c.length()>2;
PrintUtil.printTest(function1.apply("1a"));

PrintUtil.printTest(function.andThen(function1).apply(111));
//andThen类似consumer,是前一个function执行后结果作为参数传新生成的function执行,结构:true

Function<Integer,Integer> function2 = c -> c*c;
//compose和andThen正好逻辑相反,传入的参数function先执行后范围结果作为参数传给新生成的function执行
PrintUtil.printTest(function.compose(function2).apply(2));
//先执行function2,返回结果作为参数再执行function,结果:== 4
PrintUtil.printTest(function.compose(function2).andThen(function1).apply(2));
//先执行function2,其次执行funciton,最后执行function1,结果:true
PrintUtil.printTest(function2.compose(function2).apply(2));
//先执行第二个function2,返回结果作为参数再执行第一个function2,结果:16
//递归的实现又多了种办法

Function<String,String> function3 = Function.identity();//static方法
PrintUtil.printTest(function3.apply("hello"));
//identity定义了一个只返回输入参数的function,结果:hello

U7VrAzf.jpg!web

Predicate,是一个条件判断接口,接收一个T参数范围boolean值,默认抽象方法test(t);and\or\negate分别对应逻辑与、或、非操作,isEqual。

@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);
    }
    //获取该对象否定的Predicate,相当于逆转boolean
    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);
    }
	//生成一个判断对象是否相等的Predicate
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

测试用例:

Predicate<ICar> carHas4Wheel = a -> a.getWheelCount() > 3;
ICar weilai = new WeiLaiCar(3);

PrintUtil.printTest(carHas4Wheel.test(weilai));
//false
PrintUtil.printTest(carHas4Wheel.negate().test(weilai));
//true
PrintUtil.printTest(carHas4Wheel.and(b -> b.getWheelCount()>2).test(weilai));
//false
PrintUtil.printTest(carHas4Wheel.and(b -> b.getWheelCount()>2).test(new WeiLaiCar(4)));
//true
PrintUtil.printTest(carHas4Wheel.or(b -> b.getWheelCount()>2).test(new WeiLaiCar(3)));
//true

PrintUtil.printTest(Predicate.isEqual(weilai).test(weilai));
//true
PrintUtil.printTest(Predicate.isEqual(weilai).test(new WeiLaiCar(3)));
//false

Supplier,是一个只有返回没有参数的接口,只有一个方法get。

@FunctionalInterface
public interface Supplier<T> {
    /**
     * Gets a result.
     * @return a result
     */
    T get();
}

测试用例:

Supplier<Integer> supplier = () -> 1 ;
PrintUtil.printTest(supplier.get());
//1
Supplier<ICar> carSupplier = () -> new WeiLaiCar(5) ;
PrintUtil.printTest(carSupplier.get() + " : " + carSupplier.get().getWheelCount());
//com.ts.util.optional.WeiLaiCar@32a1bec0 : 5

vYfqEz6.jpg!web

Consumer<T> Predicate<T> Supplier<T> Function<T, R> BiFunction<T, U, V> ,这里的T U R V并没有特别的定义只是一种约定,就像驼峰命名和泛型中K V E一样。

分享连接

除了介绍的几种函数式接口外,java8在这几个基础上封装了很多的延伸接口,如BiConsumer\ DoubleConsumer\ IntConsumer\ LongConsumer\ ObjIntConsumer等。平时写代码时注意积累,闲的时候多去看看API,慢慢的就掌握了。

Lambda和Functional的结合很大一点就是代码简洁了,看着非常的赏心悦目。JAVA一个纯面向对象的语言,在行级代码上一直是非常的简洁易于调试,而这两个新特性的出现让行级代码的复杂度急剧提升。

相关用例代码已托管Github: play-java-sample


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK