• 009-jdk1.8版本新特性一-展方法,Lambda表达式,函数式接口、方法引用构造引用


    一、JDK1.8

    名称:Spider(蜘蛛)

    发布日期:2014-03-18

    新特性:

    1.1、扩展方法【接口的默认方法

      Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法.

      在Java中只有单继承,如果要让一个类赋予新的特性,通常是使用接口来实现,在C++中支持多继承,允许一个子类同时具有多个父类的接口与功能,在其他语言中,让一个类同时具有其他的可复用代码的方法叫做mixin。新的Java 8 的这个特新在编译器实现的角度上来说更加接近Scala的trait。 在C#中也有名为扩展方法的概念,允许给已存在的类型扩展方法,和Java 8的这个在语义上有差别。

    示例:

    interface Formula {
        double calculate(int a);
        default double sqrt(int a) {
            return Math.sqrt(a);
        }
    }
    public class ExtendMethod {
        public static void main(String[] args) {
            Formula formula = new Formula() {
                @Override
                public double calculate(int a) {
                    return sqrt(a * 100);
                }
            };
            System.out.println(formula.calculate(100));     // 100.0
            System.out.println(formula.sqrt(16));           // 4.0
        }
    }
    View Code

      Formula接口在拥有calculate方法之外同时还定义了sqrt方法,实现了Formula接口的子类只需要实现一个calculate方法,默认方法sqrt将在子类上可以直接使用。

      文中的formula被实现为一个匿名类的实例,该代码非常容易理解,6行代码实现了计算 sqrt(a * 100)。

    1.2、Lambda表达式

      “Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。

      在java中使用lambda表达式替换匿名类中的函数,使用“() -> {}”代码块替代了整个匿名类中的某个方法函数。

    示例1,从匿名类到lambda表达式

      1、匿名类排序

    List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
    Collections.sort(names, new Comparator<String>() {
        @Override
        public int compare(String a, String b) {
            return b.compareTo(a);
        }
    });

      只需要给静态方法 Collections.sort 传入一个List对象以及一个比较器来按指定顺序排列。通常做法都是创建一个匿名的比较器对象然后将其传递给sort方法。

      2、java8后lambda表达式

    Collections.sort(names, (String a, String b) -> {
        return b.compareTo(a);
    });

      3、继续优化,对于单行代码只有一个返回值,可以去掉大括号{}以及return关键字

    Collections.sort(names, (String a, String b) -> b.compareTo(a));

      4、参数类型推断,Java编译器可以自动推导出参数类型。

    Collections.sort(names, (a, b) -> b.compareTo(a));

    示例2、多线程的lambda表达式

            // Java 8 传统方式:
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Java 8 传统方式");
                }
            }).start();
    
            // Java 8 lambda方式:
            new Thread(() -> System.out.println("Java 8 lambda方式!") ).start();
    
            // Java 8 lambda方式:
            Runnable runnable = () -> System.out.println("Java 8 lambda方式!多线程");
            new Thread(runnable).start();

    示例3、遍历for forEach

            // Java 8之前:
            List<String> features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
            for (String feature : features) {
                System.out.println(feature);
            }
    
            // Java 8之后:
            List<String> features2 = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
            features2.forEach(n -> System.out.println(n));
    
            // 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示
            features2.forEach(System.out::println);

    示例4、Lambda在Collections中的用法

            List<Integer> myList = new ArrayList<>();
            for(int i=0; i<100; i++) myList.add(i);
    
            //有序流
            Stream<Integer> sequentialStream = myList.stream();
    
            //并行流
            Stream<Integer> parallelStream = myList.parallelStream();
    
            //使用lambda表达式,过滤大于90的数字
            Stream<Integer> highNums = parallelStream.filter(p -> p > 90);
            //lambdag表达式 forEach循环
            highNums.forEach(p -> System.out.println("大于90的数 并行="+p));
    
            Stream<Integer> highNumsSeq = sequentialStream.filter(p -> p > 90);
            highNumsSeq.forEach(p -> System.out.println("大于90的数 有序="+p));

    lambda作用域

    1、访问外部局部变量【可以访问 后续不能修改,final型】

            int num = 1;//等价于 final int num = 1;  固可读,不可修改,即隐性的具有final的语义
            Converter<Integer, String> stringConverter =
                    (from) -> String.valueOf(from + num);
            stringConverter.convert(2);     // 3

    2、访问对象内字段与静态变量

    和本地变量不同的是,lambda内部对于实例的字段以及静态变量是即可读又可写。该行为和匿名对象是一致的:

    class Lambda4 {
        static int outerStaticNum;
        int outerNum;
        void testScopes() {
            Converter<Integer, String> stringConverter1 = (from) -> {
                outerNum = 23;
                return String.valueOf(from);
            };
    
            Converter<Integer, String> stringConverter2 = (from) -> {
                outerStaticNum = 72;
                return String.valueOf(from);
            };
        }
    }

    3、Lambda表达式中是无法访问到默认方法的

    interface Formula {
        double calculate(int a);
        default double sqrt(int a) {
            return Math.sqrt(a);
        }
    }
    public class ExtendMethod {
        public static void main(String[] args) {
            Formula formula = new Formula() {
                @Override
                public double calculate(int a) {
                    return sqrt(a * 100);
                }
            };
    
            //Formula formula2 = (a) -> calculate( a * 100); //报错
            //Formula formula2 = (a) -> sqrt( a * 100); //报错
    
            System.out.println(formula.calculate(100));     // 100.0
            System.out.println(formula.sqrt(16));           // 4.0
    
        }
    }

    1.3、函数式接口 

    函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

    函数式接口可以被隐式转换为lambda表达式。

    函数式接口可以现有的函数友好地支持 lambda。

    1、定义

      包含函数式的设计,接口需带有@FunctionalInterface的注解。它注解在接口层面,且注解的接口要有且仅有一个抽象方法。具体就是说,注解在Inteface上,且interface里只能有一个抽象方法,可以有default方法。因为从语义上来讲,一个函数式接口需要通过一个***逻辑上的***方法表达一个单一函数。单一不是说限制你一个interface里只有一个抽象方法,单是多个方法的其他方法需要是继承自Object的public方法,或者你要想绕过,就自己实现default。函数式接口自己本身一定是只有一个抽象方法。同时,如果是Object类的public方法,也是不允许的。

    示例查看定义:

    // 错误:no target method found
    @FunctionalInterface
    public interface Func{
    }
    
    // 正确
    @FunctionalInterface
    public interface Func1{
        void run();
    }
    
    // 错误:含有多个抽象方法
    @FunctionalInterface
    public interface Func2{
        void run();
        void foo();
    }
    
    // 错误:no target method found,equals 方法签名是Object类的public方法
    @FunctionalInterface
    public interface Func3{
        boolean equals(Object obj);
    }
    
    // 正确
    @FunctionalInterface
    public interface Func4{
        boolean equals(Object obj);
        void run();
    }
    // 错误:可以是Object的public方法,而clone是protected的,这里相当于有两个抽象方法
    @FunctionalInterface
    public interface Func5{
        Object clone();
        void run();
    }
    View Code

    2、系统函数式接口

    JDK 1.8之前已有的函数式接口:

    • java.lang.Runnable
    • java.util.concurrent.Callable
    • java.security.PrivilegedAction
    • java.util.Comparator
    • java.io.FileFilter
    • java.nio.file.PathMatcher
    • java.lang.reflect.InvocationHandler
    • java.beans.PropertyChangeListener
    • java.awt.event.ActionListener
    • javax.swing.event.ChangeListener

    JDK 1.8 新增加的函数接口:

    • java.util.function

    java.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有:

    序号    接口 & 描述
    1    BiConsumer<T,U>
    代表了一个接受两个输入参数的操作,并且不返回任何结果
    
    2    BiFunction<T,U,R>
    代表了一个接受两个输入参数的方法,并且返回一个结果
    
    3    BinaryOperator<T>
    代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
    
    4    BiPredicate<T,U>
    代表了一个两个参数的boolean值方法
    
    5    BooleanSupplier
    代表了boolean值结果的提供方
    
    6    Consumer<T>
    代表了接受一个输入参数并且无返回的操作
    
    7    DoubleBinaryOperator
    代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。
    
    8    DoubleConsumer
    代表一个接受double值参数的操作,并且不返回结果。
    
    9    DoubleFunction<R>
    代表接受一个double值参数的方法,并且返回结果
    
    10    DoublePredicate
    代表一个拥有double值参数的boolean值方法
    
    11    DoubleSupplier
    代表一个double值结构的提供方
    
    12    DoubleToIntFunction
    接受一个double类型输入,返回一个int类型结果。
    
    13    DoubleToLongFunction
    接受一个double类型输入,返回一个long类型结果
    
    14    DoubleUnaryOperator
    接受一个参数同为类型double,返回值类型也为double 。
    
    15    Function<T,R>
    接受一个输入参数,返回一个结果。
    
    16    IntBinaryOperator
    接受两个参数同为类型int,返回值类型也为int 。
    
    17    IntConsumer
    接受一个int类型的输入参数,无返回值 。
    
    18    IntFunction<R>
    接受一个int类型输入参数,返回一个结果 。
    
    19    IntPredicate
    :接受一个int输入参数,返回一个布尔值的结果。
    
    20    IntSupplier
    无参数,返回一个int类型结果。
    
    21    IntToDoubleFunction
    接受一个int类型输入,返回一个double类型结果 。
    
    22    IntToLongFunction
    接受一个int类型输入,返回一个long类型结果。
    
    23    IntUnaryOperator
    接受一个参数同为类型int,返回值类型也为int 。
    
    24    LongBinaryOperator
    接受两个参数同为类型long,返回值类型也为long。
    
    25    LongConsumer
    接受一个long类型的输入参数,无返回值。
    
    26    LongFunction<R>
    接受一个long类型输入参数,返回一个结果。
    
    27    LongPredicate
    R接受一个long输入参数,返回一个布尔值类型结果。
    
    28    LongSupplier
    无参数,返回一个结果long类型的值。
    
    29    LongToDoubleFunction
    接受一个long类型输入,返回一个double类型结果。
    
    30    LongToIntFunction
    接受一个long类型输入,返回一个int类型结果。
    
    31    LongUnaryOperator
    接受一个参数同为类型long,返回值类型也为long。
    
    32    ObjDoubleConsumer<T>
    接受一个object类型和一个double类型的输入参数,无返回值。
    
    33    ObjIntConsumer<T>
    接受一个object类型和一个int类型的输入参数,无返回值。
    
    34    ObjLongConsumer<T>
    接受一个object类型和一个long类型的输入参数,无返回值。
    
    35    Predicate<T>
    接受一个输入参数,返回一个布尔值结果。
    
    36    Supplier<T>
    无参数,返回一个结果。
    
    37    ToDoubleBiFunction<T,U>
    接受两个输入参数,返回一个double类型结果
    
    38    ToDoubleFunction<T>
    接受一个输入参数,返回一个double类型结果
    
    39    ToIntBiFunction<T,U>
    接受两个输入参数,返回一个int类型结果。
    
    40    ToIntFunction<T>
    接受一个输入参数,返回一个int类型结果。
    
    41    ToLongBiFunction<T,U>
    接受两个输入参数,返回一个long类型结果。
    
    42    ToLongFunction<T>
    接受一个输入参数,返回一个long类型结果。
    
    43    UnaryOperator<T>
    接受一个参数为类型T,返回值类型也为T。
    View Code

    3、常用函数式接口使用

    1️⃣、Function<T, R> 功能型函数式接口

    T:入参类型,R:出参类型

    调用方法:R apply(T t); 

    定义函数示例:Function<Integer, Integer> func = p -> p * 10;    // 输出入参的10倍

    调用函数示例:func.apply(10);    // 结果100

    接口定义说明:

    package java.util.function;
    import java.util.Objects;
    
    @FunctionalInterface
    public interface Function<T, R> {
        // 将参数赋予给相应方法
        R apply(T t);
    
        // 先执行参数(即也是一个Function)的,再执行调用者(同样是一个Function)
        default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
           Objects.requireNonNull(before);
           return (V v) -> apply(before.apply(v));
        }
    
        // 先执行调用者,再执行参数,和compose相反。
        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, Integer> times2 = i -> i*2;
            Function<Integer, Integer> squared = i -> i*i;
    
            System.out.println(times2.apply(4));
            System.out.println(squared.apply(4));
    
            //先4×4然后16×2,先执行apply(4),在times2的apply(16),先执行参数,再执行调用者。
            System.out.println(times2.compose(squared).apply(4));  //32
    
            // 先4×2,然后8×8,先执行times2的函数,在执行squared的函数。
            System.out.println(times2.andThen(squared).apply(4));  //64
    
            // 取出 Function.identity().compose(squared) 的 squared执行,结果 4x4
            System.out.println(Function.identity().compose(squared).apply(4));   //16
            // 取出 Function.identity().apply(4) 的 4执行,结果 4
            System.out.println(Function.identity().apply(4));   //4

    理解:Function类,是一个方法,类似c++里面函数指针,一个变量可以指向一个方法,并且可以把两个方法组合起来使用(使用compose和andThen),而可以通过identity这个静态方法来获取当前执行的方法。

    2️⃣、Predicate<T>断言型函数式接口

    T:入参类型;出参类型是Boolean 

    调用方法:boolean test(T t);

    定义函数示例:Predicate<Integer> predicate = p -> p % 2 == 0;    // 判断是否、是不是偶数

    调用函数示例:predicate.test(100);    // 运行结果true

    接口定义说明

    package java.util.function;
    import java.util.Objects;
    
    // 一个谓词,布尔类型函数
    @FunctionalInterface
    public interface Predicate<T> {
    
        // 根据给定参数 获取布尔值
        boolean test(T t);
    
        // and 与 &&
        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);
        }
    
        // or 或者
        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);
        }
    }

    示例

            String tempStr="Spring is A";
            Predicate<String> length5=p->p.length()>=5;
            Predicate<String> containA=p->p.contains("A");
            Predicate<String> containB=p->p.contains("B");
            // 长度大于5 true
            System.out.println(length5.test(tempStr));
            // 包含A  true
            System.out.println(containA.test(tempStr));
    
            // 长度大于5 and 包含A  true
            System.out.println(length5.and(containA).test(tempStr));
            // 长度大于5 and 包含B  false
            System.out.println(length5.and(containB).test(tempStr));
            // 长度大于5 or 包含B  false
            System.out.println(length5.or(containB).test(tempStr));
            // 长度大于5 取反  false
            System.out.println(length5.negate().test(tempStr));
            // 长度大于5 取反  false
            System.out.println(!length5.test(tempStr));

    3️⃣、Supplier<T>供给型函数式接口

    T:出参类型;没有入参 

    调用方法:T get();

    定义函数示例:Supplier<Integer> supplier= () -> 100;    // 常用于业务“有条件运行”时,符合条件再调用获取结果的应用场景;运行结果须提前定义,但不运行。

    调用函数示例:supplier.get();

    接口定义说明:

    package java.util.function;
    
    // 生产型
    @FunctionalInterface
    public interface Supplier<T> {
    
        //得到一个结果
        T get();
    }

    示例

        public static String supplierTest(Supplier<String> supplier) {
            return supplier.get();
        }
        public static void main(String[] args) {
            String name = "测试";
            // () -> name.length() 无参数,返回一个结果(字符串长度)
            // 所以该lambda表达式可以实现Supplier接口
            System.out.println(supplierTest(() -> name.length() + ""));
        }

    4️⃣、Consumer<T>消费型

    T:入参类型;没有出参 

    调用方法:void accept(T t);

    定义函数示例:Consumer<String> consumer= p -> System.out.println(p);    // 因为没有出参,常用于打印、发送短信等消费动作

    调用函数示例:consumer.accept("18800008888");

    接口方法定义

    package java.util.function;
    import java.util.Objects;
    
    @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); };
        }
    }

    示例

       public static void modifyTheValue3(int value, Consumer<Integer> consumer) {
            consumer.accept(value);
        }
    
        public static void main(String[] args) {
            // (x) -> System.out.println(x * 2)接受一个输入参数x
            // 直接输出,并没有返回结果
            // 所以该lambda表达式可以实现Consumer接口
            modifyTheValue3(3, (x) -> System.out.println(x * 2));
        }

     1.4、方法引用和构造引用

      方法引用是对Lambda表达式符合某种情况下的一种缩写,使得我们的Lambda表达式更加的精简, 也可以理解为Lambda表达式的另一种表现形式(缩写)

      使用 :: 关键字来传递方法或者构造函数引用

    方法引用

    1、静态引用【指向静态方法的方法引用】类名::静态方法名

    @FunctionalInterface
    interface Converter<F, T> {
        T convert(F from);
    }
    public class TestReference {
        public static void main(String[] args) {
            //静态引用
            Converter<String, Integer> converter = Integer::valueOf;
            Integer converted = converter.convert("123");
            System.out.println(converted);   // 123
        }
    }

    2、指向任意类型实例方法的方法引用(这个实例为方法的参数)【类名::实例方法名

            //指向任意类型实例方法的方法引用(这个实例为方法的参数)
            List<String> list = Arrays.asList("av","asdf","sad","324","43");
            List<String> collect = list.stream().map(String::toUpperCase).collect(Collectors.toList());
            System.out.println(collect);

    3、向现有对象的实例方法的方法引用(这个实例为外部对象)【实例对象名::实例方法名

            //指向现有对象的实例方法的方法引用(这个实例为外部对象)
            List<String> list2 = Arrays.asList("av","asdf","sad","324","43");
            String a="avasdf";
            List<String> collect2 = list2.stream().filter(a::contains).collect(Collectors.toList());
            System.out.println(collect2);

    典型示例

            //传统Lambda表达式
            Consumer<String> consumer = (x) -> System.out.println(x);
            consumer.accept("Hi: 我是Lambda表达式实现的!");
            //方法引用实现
            consumer = System.out::println;
            consumer.accept("Hello : 我是使用方法引用实现的 ");

    4、构造引用【类名 :: new

      与函数式接口相结合,自动与函数式接口中方法兼容。 可以把构造器引用赋值给定义的方法。 

            //传统Lambda方式
            Supplier<Map> mapSupplier = ()-> new HashMap<String,String>();
            Map map = mapSupplier.get();
            System.out.println(map);
    
            //构造器引用
            mapSupplier = HashMap::new;
            map = mapSupplier.get();
            System.out.println(map);
  • 相关阅读:
    java数据库连接池dbcp的使用
    图片轮显效果大全
    W5500问题集锦(持续更新中)
    Gamma校正及其OpenCV实现
    GlusterFS源代码解析 —— GlusterFS 日志
    cocos2dx 以子弹飞行为例解说拖尾效果类CCMotionStreak
    leetcode__Convert Sorted List to Binary Search Tree
    昨天面试新浪 java试题
    linux概念之性能调优
    Java实现 黑洞数
  • 原文地址:https://www.cnblogs.com/bjlhx/p/9711292.html
Copyright © 2020-2023  润新知