JDK8新特性之Lambda表达式详解

JDK8新特性之Lambda表达式

  • 1 Java语言发展史
  • 2 什么是Lambda表达式
  • 3 Lambda表达式语法
  • 4 什么情况下使用Lambda表达式
  • 5 函数式接口
    • 5.1 Predicate接口
    • 5.2 Consumer 接口
    • 5.3 Function接口
    • 5.4 对原始类型的处理
  • 6 捕获Lambda
  • 7 方法引用
    • 7.1 什么是方法引用
    • 7.2 如何构建方法引用
    • 7.3 构造方法引用
  • 8 Lambda表达式练习

1 Java语言发展史

  1991 年, SUN 公司中 James Goslin 带领团队启动”Green”项目.开发 了一种称为”Oak”语言
  1995 年, SUN 公司把 Oak 语言改名为 Java.
  2004 年 9 月, 发布 J2SE1.5. 为了表示该版本的重要性,把 J2SE1.5 更名为   JavaSE 5.0 2014 年, 发布了 Java8 正式版
  2018 年 9 月发布了 Java11.
  Oracle 支持 Java8 到 2025 年, 支持 Java11 到 2026 年.

2 什么是Lambda表达式

  Lambda 是数学中的一个函数. Java 中使用方法来代替函数,方法总 是作为类或对象的一部分存在的. 可以把 Lambda 看作是一个匿名方法, 拥有更简洁的语法。

3 Lambda表达式语法

  语法: (参数列表) -> {语句;}
  Lambda 表达式由参数列表和一个 Lambda 体组成, 通过箭头连接
  说明:
  1) 当只有一个参数时, 参数列表的小括弧可以省略 x -> { System.out.println(x); }

  2) 参数列表中参数的数据类型可以省略 ( x,y ) -> {x.compareTo(y);}

  3) 如果 Lambda 体只有一条语句,大括弧也可以省略 x -> { return x + 2 ; }

  4) 如果Lambda 体中只有一条return 语句, return 关键字可以省略 (x,y) -> x+y

  通过举例说明(以下都是有效的Llambda表达式语法):

    (String s ) -> s.length()

    (Student stu) -> stu.getAge() > 18

     (int x, int y ) -> {
       System.out.print(“result”);
       System.out.println( x + y );
     }

    () -> {}

    () -> “hehe”

  但是注意:

  (String s) -> { “hello” } //不合法,因为“hello”不是一个表达式

如果想要合法,可以改为以下写法:

   (String s) -> { return “hello” ; }

   (String s) -> “hello”

4 什么情况下使用Lambda表达式

  ①布尔表达式, 判断参数接收的 list 集合是否为空
   ( List list) -> list.is mpty()

  ②创建对象,并返回
   () -> new Student()

  ③消费(使用)一个对象, 把参数接收学生对象的姓名打印出来
   (Student stu ) -> { System.out.println( stu.name) ; }

   ④从 一个对象中选择, 返回参数对象的成绩
   (Student stu) -> stu.getScore()

   ⑤组合两个值 (int a, int b) -> a*b 比较两个对象
   (Student stu1, Student stu2) -> stu1.getScore() - stu2.getScore()

5 函数式接口

   在 JDK8 中, 引用了函数式接口. 就是只定义一个抽象方法的接口. 如: Comparator 接口 , Runnable 接口

@FunctionalInterface //注解,声明接口为函数式接口 
public interface Adder{ 
	int add(int x, int y); 
}
//当前ByteAdder接口不是函数式接口, 从Adder接口中继承了一个抽象方法,在本接口中又定义了一个抽象方法
public interface ByteAdder extends Adder{ 
	
	byte add( byte b1, byte b2); 
}
//当前 Nothing 接口也不函数式接口,因为没有抽象方法
 public inteface Nothing{ }

  函数式接口就是为 Lambda 表达式准备的,或者说 Lambda 表达式 必须实现一个函数式接口
   java.util.function 包中定义了一些基本的函数式接口,如 Predicate, Consumer, Function,Supplier 等

5.1 Predicate接口


package java.util.function;

import java.util.Objects;

@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接口中定义一个抽象方法 test(T ),接收一个 T 类型的 对象参数,返回一个布尔值 当需要一个涉及类型 T 的布尔表达式时,可以使用这个接口

public class Test01 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("lisi", "zhangsan", "wangwu", "chenqi", "maliu");
        List<String> result = filter(list, x -> x.length() > 6);
        System.out.println(result);
    }//定义方法, 方法可以把 List 列表中符合条件的元素存储到一个新的 List 列表中返回 

    public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        //遍历 list 参数列表,把符合 predicate 条件的元素存储到 result 中 
        for (T t : list) {
            if (predicate.test(t)) {
                result.add(t);
            }
        }
        return result;
    }
}

5.2 Consumer 接口

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

  Consumer接口定义了一个 accept(T)抽象方法,可以接收一个 T 类型的对象,没有返回值. 如果需要访问类型 T 的对象,对该对象做一 些操作,就可以使用这个接口.
  在 Collection 集合和 Map 集合中都有 forEach(Consumer)方法

public class Test02 {
    public static void main(String[] args) {
    
        List<String> list = Arrays.asList("lisi", "gg", "jj", "tuantuan", "daimeir", "timo", "XDD");
        list.forEach(s -> System.out.println(s));
        
        Map<String, Integer> map = new HashMap<>();
        map.put("lisi", 22);
        map.put("feifei", 28);
        map.put("zhangxiaosan", 20);
        map.put("chenqi", 30);
        map.forEach((k, v) -> System.out.println(k + "->" + v));
    }
}

5.3 Function接口

import java.util.Objects;

@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接口中定义了accept( T )方法,接收一个T类型的参数对象,返回一个 R 类型的数据. 如果需要定义一个 Lambda,将一个输入 对象的信息加工后映射到输出,就可以使用该接口

public class Test03 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("lisi", "gg", "jj", "tuantuan", "daimeir", "timo", "XDD");
        //把 list 集合中存储字符串的长度映射出来
        List<Integer> result = map(list, x -> x.length());
        System.out.println(result);
    }
    // 把一个 List 列表映射到另外一个 List 列表中


    public static <T, R> List<R> map(List<T> list, Function<T, R> function) {
        List<R> result = new ArrayList<>();
        //存储映射后的数据
        for (T t : list) {
            // 把 apply 对 t 对象的操作结果保存 到 result 集合中
            result.add(function.apply(t));
        }
        return result;
    }
}

5.4 对原始类型的处理

  泛型只能绑定引用类型,不能使用基本类型 Java8 中函数式接口为基本类型也提供了对应的接口,可以避免在 进行输入输出原始数据时频繁进行装箱/拆箱操作. 一般来说,在针对专门的基本类型数据的函数式接口名称前面加 上 了 对应的原始类型前缀
  如 : IntPredicate, IntConsumer,IntFunction 等.

public class Test04 {
    public static void main(String[] args) {
        IntPredicate evenNumbers = (int x ) -> x % 2 == 0 ;
        boolean test = evenNumbers.test(10);
        System.out.println(test);
    }
}

6 捕获Lambda

  Lambda 表达式可以使用外层作用域中定义的变量,如成员变量,局 部变量,称为捕获 Lambda.

public class Test05 {
    int xx = 123; //实例变量
    static int yy = 456; //静态变量

    public static void main(String[] args) {

        IntUnaryOperator operator = i -> {
            return i + yy; //把参数接收的数据与静态变量的值相加并返回
        };
        System.out.println( operator.applyAsInt(10));


        //在 Lambda 表达式中使用局部变量,局部变量必须是 final 修饰,或者是事实上的 final
        int zz = 789; //局部变量
        final int ff = 147; //final 修饰的局部变量
        IntUnaryOperator operator2 = i->{
            return i + ff; 
        };
        System.out.println(operator2.applyAsInt(10));

        //zz 虽然没有使用 final 修饰,如果它是事实的 final,后面没有 修改 zz 值的代码 
        operator2 = i ->{ 
            return i + zz;
        }; 
        System.out.println( operator2.applyAsInt(10)); 
        // zz = 258; //如果再对 zz 重新赋值,则上面的 lambda 表达式语法错误
    }
}

  main方法中只能使用静态变量,在实例方法中也可以使用成员变量 xx.

7 方法引用

7.1 什么是方法引用

  方法引用可以让你重复使用现有的方法定义, 并像 Lambda 一样 传递它们. 如:
  list.forEach( x -> System.out.println(x) );
  使用方法引用可以这样:
  list.forEach( System.out :: println );

  方法引用可以看作是仅仅调用特定方法的 Lambda 表达式的一种 快捷写法. 需要使用方法引用时, 目标引用放在分隔符 ::前面, 方法名放在:: 的后面, 注意,只需要方法名不需要小括弧

  (Stude t stu) -> stu getScore()
改为方法引用 :
  Stude t :: getScore ()

  Thread.currentThread().dumpStack()
改 为 方 法 引 用 :
  Thread.currentThread() :: dumpStack

  (Str, i) -> Str.substring(i)
改为方法引用:
   String :: substring

  a -> System.out.println(a)
改为方法引用:
  System.out :: prinltn

7.2 如何构建方法引用

  方法引用主要有三类:
   1)指向静态方法的方法引用

在这里插入图片描述

Integer[] data ={65,23,87,2,34,99}; 
Arrays.sort( data, Integer::compare );

Arrays.sort()方法,需要传入一个Comparator比较器,Comparator是一个函数式接口,函数式接口可以赋值Lambda表达式

public static <T> void sort(T[] a, Comparator<? super T> c) {
    if (c == null) {
        sort(a);
    } else {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, c);
        else
            TimSort.sort(a, 0, a.length, c, null, 0, 0);
    }
}

Integer接口的compareTo()方法和compare()方法。

compareTo(Integer anotherInteger) {
    return compare(this.value, anotherInteger.value);
}

public static int compare(int x, int y) {
    return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

  2) 指向任意类型的实例方法的引用

Lambda 表达式 (args0, args1) -> args0.instanceMethod(args1) 
args0 是 Classname 类型的一个对象 
方法引用: Classname :: instanceMethod

  3) 指向现有对象的实例方法的引用
   Lambda 表达式: (args) -> obj.instanceMethod(args)
   方法引用: obj::instanceMethod

public class Test06 {
   public static void main(String[] args) {

       Integer[] data ={65,23,87,2,34,99};
       //引用静态方法
       Arrays.sort( data, Integer::compare );

        System.out.println(Arrays.toString(data));


       List<String> list = Arrays.asList("a","b");
       //之前只能传入匿名内部类对象
       list.sort(new Comparator<String>() {
           @Override
           public int compare(String o1, String o2) {
               return o1.compareTo(o2);
           }
       });
       //现在可以传入Lambda表达式 因为 Comparator 是一个函数式接口
       list.sort((a,b)->{return a.compareTo(b);});

       //传入方法引用
       //引用实例方法
       list.sort(String::compareTo);

       //对 System.out 这个实例方法的引用
       list.forEach(System.out::println); 
       
   }
}

7.3 构造方法引用

  对于一个现有的构造方法,可以使用类名和关键字 new 来创建 一个构造方法的引用: Classname::new

public class Test07 {
   public static void main(String[] args) {
       
       //1)引用无参构造方法
       Supplier<Person> supplier = Person::new;
       Person p1 = supplier.get();
       System.out.println(p1);
       
       //2)引用有一个参数的构造方法
       Function<String, Person> function = Person::new;
       Person p2 = function.apply("lambda");
       System.out.println(p2);

       //3)引用有两个参数的构造方法
        /BiFunction<String, Integer, Person> biFunction = Person::new;
       Person p3 = biFunction.apply("lambda", 28);
       System.out.println(p3);

       //4)如果引用有三个参数及三个以上参数的构造方法,需要自定义匹配的函数式接口
       TriFunction<String, Integer, String, Person> triFunction = Person::new;
       Person p4 = triFunction.apply("lambda", 35, "男");
       System.out.println(p4);
   }
}
//@FunctionalInterface不加也可以,apply方法名可随意定义
@FunctionalInterface
interface TriFunction<T, U, V, R> {
   R apply(T t, U u, V v);
}

class Person {
   String name;
   int age;
   String gender;

   public Person(String name, int age, String gender) {
       this.name = name;
       this.age = age;
       this.gender = gender;
   }

   public Person(String name, int age) {
       this.name = name;
       this.age = age;
   }

   public Person(String name) {
       this.name = name;
   }

   public Person() {
   }

   @Override
   public String toString() {
       return "Person{" + "name='" + name + '\'' + ", age=" + age + ", gender='" + gender + '\'' + '}';
   }
}

8 Lambda表达式练习

public class Test08 {
   public static void main(String[] args) {
       //定义 List 集合存储
       List<Student> list = new ArrayList<>();
       list.add(new Student("lisi", 80));
       list.add(new Student("zhangsan", 30));
       list.add(new Student("wangwu", 90));
       list.add(new Student("feifei", 60));
       list.add(new Student("mingge", 100));
       System.out.println(list);
       
       //使用匿名内部类排序
       list.sort(new Comparator<Student>() {
           @Override
           public int compare(Student o1, Student o2) {
               return o1.name.compareTo(o2.name);
           }
       });
       System.out.println(list);

       //使用 Lambda 表达式
       list.sort((p1, p2) -> p2.name.compareTo(p1.name));
       System.out.println(list);

       //Comparator 接口中有一个 comparing 静态方法返回 Comparator 比较器
       list.sort(Comparator.comparing((stu) -> stu.name));
       System.out.println(list);

       //方法引用,Student 类中 getScore 方法返回成绩
       list.sort(Comparator.comparing(Student::getScore));
       System.out.println(list);

   }
}

class Student {
   String name;
   int score;

   public Student(String name, int score) {
       this.name = name;
       this.score = score;
   }

   @Override
   public String toString() {
       return "Student{" + "name='" + name + '\'' + ", score=" + score + '}';
   }

   public int getScore() {
       return score;
   }
}

你可能感兴趣的:(JDK8)