Java8-Lambda表达式详解

Java8-Lambda

1. Lambda表达式

1.1 Lambda表达式介绍

1.1.1 lambda表达式作用

  • lambda表达式是Java8的一个新特性,当我们在需要使用实现了某些接口的实例时,即便是该实例只在某一处使用,我们也得为它新建一个实现类(最起码也得使用匿名类来创建该实例)
  • 自JDK8开始,提供了lambda表达式语法特性,能够极大地简化代码量,在线程创建,集合Stream操作等有着广泛应用

1.2 函数式接口

1.2.1 函数式接口介绍

(1)什么是函数式接口?

  • 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口
    • 在JDK8中,接口中提供了default方法,使用default接口修饰的方法在接口定义中有默认的实现,表示该接口的实现类中可以不去实现该方法而使用接口的默认实现;
    • 要是用lambda表达式,需要接口的某一个方法必须要靠类去实现,被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用
  • 函数式接口可以被隐式转换为 lambda 表达式。

(2)通过注解表示函数式接口

  • 可以在任意函数式接口上使用 @FunctionalInterface注解,这样做可以检测它是否是一个函数式接口,同时 javadoc也会包含一条声明,说明这个接口是一个函数式接口。

    • 当我们在接口中使用@FunctionalInterface,编译器会自动检查该接口是否符合定义,如果不符合函数式接口定义会报错

    • 在实际开发者有两个比较常见的函数式接口:Runnable接口,Comparator接口

      @FunctionalInterface
      public interface Runnable {
          ...
      }
      

1.3 lambda表达式的使用

1.3.1 lambda表达式与传统做法相比

  • 在介绍使用lambda表达式之前,我们先来看看原先做法是怎样的,进一步体会到lambda表达式的优点

  • 自定义函数式接口

    @FunctionalInterface
    interface GreetingService {
        void sayMessage(String message);
    }
    

(1)创建实现该接口的类

  • 传统实现方式:通过创建类来实现接口

    class GreetingServiceImpl implements GreetingService{
        
        @Override
        public void sayMessage(String message) {
            System.out.println("Hello "+message);
        }
        
    }
    
  • 实例化该接口并使用

    public class LamdaTest {
        
        public static void main(String[] args) {
            
            GreetingService greetingService = new GreetingServiceImpl();
            greetingService.sayMessage("Ni187");//Hello Ni187
        
        }
    }
    

(2)使用静态内部类

  • 通过在使用该接口的类中创建一个静态内部类内部,实现接口方法,进而使用内部类的方法

  • 实际上代码量并没有比传统方法少,因为它只是将类的声明位置换到内部而已,但是由于我们创建的接口会只在这里使用,所以使用静态内部类会提高代码的可读性

    public class LambdaTest {
        
        static class GreetingServiceImpl02 implements GreetingService{
            
            @Override
            public void sayMessage(String message){
                System.out.println("Hello "+message);
            }
            
        }
        
        public static void main(String[] args) {
            GreetingService greetingService = new GreetingServiceImpl();
            greetingService.sayMessage("Ni187");
            //Hello Ni187
        }
    }
    

(3)使用局部内部类

  • 在通过使用静态内部类时,如果只是仅仅某一个方法中需要接口的实例化对象,那么我们可以通过创建局部内部类来实例化接口,进一步提高代码的可读性

    public class LambdaTest {
    
        public static void main(String[] args) {
            
            class GreetingServiceImpl02 implements GreetingService{
                @Override
                public void sayMessage(String message){
                    System.out.println("Hello "+message);
                }
            }
            
            GreetingService greetingService = new GreetingServiceImpl02();
            greetingService.sayMessage("Ni187");
            //Hello Ni187
        }
    }
    

(4)使用匿名类

  • 什么是匿名类?

    • 当我们需要实现某个接口,或者在继承关系中需要重写父类的某个或某些方法时,我们可以在new 父类名或接口名{内容}以这种方式来简化类的声明写法,从而提高我们编码的效率

    • 匿名类的内部书写规则就如同普通类的写法,通过new之后的类名或接口名表示它是某一类的子类或者是接口的实现类,我们可以继承原来类的方法,属性等,也可以重写原来方法,或者添加某些方法,只是这个类没有具体的类名而已

      • 通过反射获取匿名类Class对象

        Class r = new Runnable {
              @Override
              public void run() {}
              public void run2() {}
        }.getClass();
        System.out.println(r);//class com.niss.LambdaTest$1
        
      • 通过反射获取局部类Class对象

        Class RunImpl implements Runnable(){
             @Override
              public void run() {}
        }
        System.out.println(RunImpl.class);//class com.niss.LambdaTest$1RunImpl
        
  • 匿名类的使用方法如下:

    public class LambdaTest {
    
        public static void main(String[] args) {
            GreetingService greetingService = new GreetingService() {
                @Override
                public void sayMessage(String message) {
                    System.out.println("Hello "+message);
                }
            };
            greetingService.sayMessage("Ni187");
            //Hello Ni187
        }
    }
    
  • 可以看出当我们只在某些特定场景使用接口时,完全可以通过创建匿名类的方式来简化编码流程

(2)lambda表达式

  • 以下是使用lambda语法进行接口的实现

    public class LambdaTest {
        public static void main(String[] args) {
            GreetingService greetingService = message->System.out.println("Hello "+message);
            greetingService.sayMessage("Ni187 ");//Hello Ni187
        }
    }
    
  • 可以看出当我们使用lambda表达式时,代码只保留最核心的部分,注重实现的逻辑,其余统统被精简掉,这就是lambda表达式的一个优势所在


1.4 lambda表达式语法

1.4.1 Lambda表达式基本语法

基本语法:

  • (parameters) ->{ statements };
  • 参数只有一个且不声明类型时可以省略()
  • 当后边方法体只有一条语句时可以省略{}
    • 当该语句为返回结果语句时,不可以加return关键字,除非不省略{}
  • 参数类型可以省略,但是必须全部省略或全部保留
  • 当引用的类方法或对象方法支持泛型时,可以在引用时加上加上泛型约束

1.4.2 使用示例

(1)常规使用

  • 接口定义

    interface GreetingService {
        String sayMessage(String message,int age);
    }
    
  • 以下表示方法都等价

    GreetingService greetingService = (message,age)->"Hello "+message+age;
    
    GreetingService greetingService = (String message,int age)->"Hello "+message+age;
    
    GreetingService greetingService = (String message,int age)->{return "Hello "+message+age;};
    

(2)常用接口通过lambda表达式进行简化构造

  • 通过Lambda表达式实现Comparator接口进行排序

    //定义的person类
    class Person{
        private int id;
        private String name;
        
        构造方法,getter,setter
    
        @Override
        public String toString() {...}
    }
    
    public class LambdaTest {
        
        public static void main(String[] args) {
            
            LinkedList<Person> persons = new LinkedList();
            persons.add(new Person(10,"Tom"));
            persons.add(new Person(20,"Bob"));
            persons.add(new Person(15,"Com"));
            persons.add(new Person(20,"Alice"));
    
            //通过使用lambda表达式,实例化Comparator接口,完成排序工作
            persons.sort((person1, person2)->{
                if(person1.getId()!=person2.getId()){
                    return person1.getId()-person2.getId();
                }else{
                    return person1.getName().compareTo(person2.getName());
                }
            });
            
            for(Person person:persons){
                System.out.println(person);
            }
        }
    }
    
    结果:
    Person{id=10, name='Tom'}
    Person{id=15, name='Com'}
    Person{id=20, name='Alice'}
    Person{id=20, name='Bob'}
    

1.5 方法的引用

1.5.1方法的引用介绍

  • 当我们需要使用接口的方法时,如果我们之前实现过类似的功能方法或者接口,我们可以利用函数引用的方式传入该方法,从而使用该方法来完成我们所要实现的接口方法功能

  • 方法引用的唯一用途是支持Lambda的简写

  • 例如以下

    persons.forEach(value-> System.out.println(value));
    
    persons.forEach(System.out::println);
    
  • 方法引用要求

    • 参数数量和类型要与需要传入接口中定义的一致
    • 接口的抽象方法没有返回值,引用的方法可以有返回值也可以没有
    • 返回值类型要与需要传入接口中定义的一致
    • 对于泛型方法泛型的声明与正常方式相同
      • 静态方法:类名::<泛型声明>静态方法
      • 成员方法:类名::<泛型声明>静态方法
      • 构造方法:类名<泛型声明>::new

1.5.2 方法引用分类

类别 使用形式
静态方法引用 类名 :: 静态方法名
实例方法引用 对象名(引用名) :: 实例方法名
类方法引用 类名 :: 实例方法名
构造方法引用 类名 :: new

(1)静态方法引用

  • 静态方法引用:引用比较的静态方法,代替comparator接口方法

    public class LambdaTest {
        
        public static int comparePerson(Person person1, Person person2){
            if(person1.getId()!=person2.getId()){
                return person1.getId()-person2.getId();
            }else{
                return person1.getName().compareTo(person2.getName());
            }
        }
        
        public static void main(String[] args) {
            LinkedList<Person> persons = new LinkedList();
            ...
            persons.sort(LambdaTest02::comparePerson);
            ...
            persons.forEach(value-> System.out.println(value));
            
        }
    }
    
  • 引用静态方法创建线程

    new Thread(pojo::say).start();
    
  • 注意引用静态方法,在调用该接口时也会对类产生影响,相当于同时调用了该类的静态方法

    public class LambdaTest {
    
        static class Pojo{
            private String name;
    
            static String str = "初始静态变量";
    
            public Pojo(String name) {
                this.name = name;
            }
    
            public Pojo(){
                this("初始名字");
            }
    
            String getName(){
                return this.name;
            }
    
            void setName(String name){
                this.name = name;
            }
    
            void setAs(Pojo pojo){
                this.name = pojo.name;
            }
    
            void say(){
                System.out.println(name+":哈哈");
            }
    
            public static String sayStaticValue(){
                System.out.println("Pojo:"+Pojo.str);
                return Pojo.str;
            }
            
            public static void setStaticValue(String str){
                Pojo.str = str;
            }
        }
    
        public static void main(String[] args) {
            Pojo pojo = new Pojo("");
            GreetingService greetingService = Pojo::setStaticValue;
            greetingService.say("引用了pojo的setStaticValue方法");//引用了pojo的set方法
            System.out.println("===");
            System.out.println(Pojo.str);
            new Thread(Pojo::sayStaticValue).start();//Pojo:引用了pojo的setStaticValue方法
        }
    }
    
    等价于》》》GreetingService greetingService = (str)->Pojo.setStaticValue(str);
    

(2)实例方法引用

  • 通过引用实例的方法来实现接口的功能

    public class LambdaTest {
        
        public static int comparePerson(Person person1, Person person2){
            if(person1.getId()!=person2.getId()){
                return person1.getId()-person2.getId();
            }else{
                return person1.getName().compareTo(person2.getName());
            }
        }
        
        public static void main(String[] args) {
            LinkedList<Person> persons = new LinkedList();
            ...
            persons.sort(new PersonComparator()::comparePerson);
            //实际上persons.sort((p1,p2)->new PersonComparator().comparePerson(p1,p2))
            ...
            persons.forEach(value-> System.out.println(value));
    
        }
    }
    
    class PersonComparator{
        public int comparePerson(Person person1, Person person2){
            if(person1.getId()!=person2.getId()){
                return person1.getId()-person2.getId();
            }else{
                return person1.getName().compareTo(person2.getName());
            }
        }
    }
    
  • 注意引用对象实例的方法之后,在调用该接口时也会对实例产生影响,相当于同时调用了该对象的方法

    public class LambdaTest {
        
        ...
            
        public static void main(String[] args) {
    
            Pojo pojo = new Pojo();
            GreetingService greetingService = pojo::setName;
            greetingService.say("引用了pojo的set方法");
            System.out.println("===");
            System.out.println(pojo.getName());//引用了pojo的set方法
        }
    }
    
    等价于》》》GreetingService greetingService = msg->pojo.setName(msg);
    
  • 如果在类中成员方法内容上可以使用this关键字与super关键字进行引用

    • this:表示使用该对象的方法进行引用

      public class MethodRefTest {
      
          public void say(){
              System.out.println("hello.");
          }
      
          public void startThread(){
              new Thread(this::say).start();
          }
      
          public static void main(String[] args) {
              new MethodRefTest().startThread();//hello.
          }
      
      }
      
    • super:表示使用父类的方法进行引用

      class Son extends MethodRefTest{
      
          public void startThreadBySon(){
              new Thread(super::say).start();
          }
      
          public static void main(String[] args) {
             new Son().startThreadBySon();
          }
      }
      

(3)类方法的引用

  • 这里使用的是:类名::实例方法名

  • 对象方法引用

    class Person{
        ...
        public int compareToAnother(Person person2){
            if(this.getId()!=person2.getId()){
                return this.getId()-person2.getId();
            }else{
                return this.getName().compareTo(person2.getName());
            }
        }
       ...
    }
    
    public class LambdaTest02 {
      
        public static void main(String[] args) {
            LinkedList<Person> persons = new LinkedList();
            ...
            persons.sort(Person::compareToAnother);
            //实质上为persns.sort((Person person1, Person person2) -> person1.compareToAnother(perosn2) );
            persons.forEach(value-> System.out.println(value));
    
        }
    }
    
    • 首先要说明的是,方法引用不是方法调用。compareToAnother一定是某个实例调用的,该实例就被作为lambda表达式的第一个参数,然后lambda表达式剩下的参数作为 compareToAnother的参数,这样compareToAnother正好符合lambda表达式的定义
    • 或者也可以这样理解:(Person person1, Person person2) -> { person1.compareToAnother(perosn2) }需要当前对象调用,然后与另外一个对象比较,并且返回一个int值。可以理解为lambda表达式的第一个参数 person1 赋值给当前对象, 然后person2赋值给 other对象,然后返回int值。

(4)构造方法引用

  • 构造方法的引用,实质上是调用了构造方法

    interface CreateInter{
        Pojo create(String name);
    }
    
    public class LambdaTest {
        
        public static void main(String[] args) {
            CreateInter createInter = Pojo::new;
            Pojo pojo = createInter.create("这是我的新名字");
            System.out.println(pojo.getName());//这是我的新名字
        }
    }
    
    • 该示例代码调用了public Pojo(String name)构造方法
  • 构造数组对象

    interface StringArrCreatable {
        public String[] createStringArr(int length);
    }
    
    class Test {
        public static void main(String[] args) {
            StringArrCreatable t2 = String[]::new;
            //实际上 StringArrCreatable t2 = (length) -> new String[length];
            String[] arr = t2.createStringArr(5);
            System.out.println(arr.length);//
        }
    }
    

1.6 函数式接口使用

1.6.1 常用函数式接口

  • jdk中提供了非常多的函数式接口,大多封装在java.util.funcation

(1)Supplier接口

  • 该接口只包含一个无参方法:T get()用于获得一个对象,源码:

    @FunctionalInterface
    public interface Supplier<T> {
    
        /**
         * Gets a result.
         *
         * @return a result
         */
        T get();
    }
    
  • 该接口用来获取一个泛型参数指定类型的对象数据

    public class SupplierTest {
    
        public static<T> T getString(Supplier<T> supplier){
            return supplier.get();
        }
    
        public static void main(String[] args) {
            String str = getString(()->"Hello,"+"world.");
    //        实际上相当于:
    //        String str = getString(new Supplier<String>() {
    //            @Override
    //            public String get() {
    //                return "Hello,"+"world.";
    //            }
    //        });
            System.out.println(str);//Hello,"+"world.
        }
    }
    
    
    

(2)Consumer接口

  • 该接口包含一个accept方法用于接收泛型指定的类型对象,然后使用该对象;还包含一个andThen默认方法用于下一步操作,实现组合操作的过程,源码:

    @FunctionalInterface
    public interface Consumer<T> {
        /**
         * Performs this operation on the given argument.
         * @param t the input argument
         */
        void accept(T t);
    
        default Consumer<T> andThen(Consumer<? super T> after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); };
        }
    }
    
  • 使用accept方法

    public class ConsumerTest {
        public static void display(String name, Consumer<String> consumer) {
            consumer.accept(name);
        }
    
        public static void main(String[] args) {
            display("你好,世界", System.out::println);//你好,世界
            //实际上相当于:
            display("你好,世界", (String o)->{
                System.out.println(o);
            });
        }
    }
    
  • 使用andThen方法

    public class ConsumerTest {
        public static void method(String name, Consumer<String> first,Consumer<String> second) {
            first.andThen(second).accept(name);
            //实际上相当于:
            //frist.accpet(name);
            //second.accept(name);
        }
    
        public static void main(String[] args) {
            method("Java:", s-> System.out.println(s.toUpperCase()), s-> System.out.println(s.toLowerCase()));
        }
    }
    

(3)Predicate接口

  • Predicate接口为断言式接口,主要用来进行条件判断,源码:

    @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);
        }
    
        @SuppressWarnings("unchecked")
        static <T> Predicate<T> not(Predicate<? super T> target) {
            Objects.requireNonNull(target);
            return (Predicate<T>)target.negate();
        }
    }
    
  • test方法为测试方法,根据传入参数与方法的实现返回测试布尔数据结果

    Predicate<Integer> bigInteger = (n)->n.compareTo(1000000)>0;
    System.out.println(bigInteger.test(10));//false
    System.out.println(bigInteger.test(1000000000));//true
    
  • andnegateor方法用于传入两个Predicate接口实例,进行条件判断(分别对应&&!,|

    Predicate<Integer> bigInteger = (n) -> n.compareTo(100) > 0;
    Predicate<Integer> evenInteger = (n) -> n % 2 == 0;
    
    evenInteger.and(bigInteger).test(1000);//true
    evenInteger.and(bigInteger).test(100);//false
    evenInteger.and(bigInteger).test(1001);//false
    evenInteger.negate().and(bigInteger).test(1001)//true
    evenInteger.or(bigInteger.negate()).test(102)//true
    
  • isEqual静态方法提供了对传入对象的相等判断,如果传入对象为空,返回测试参数是否为空,否则调用传入对象的equals方法

    Predicate.isEqual("String").test("String")//true
    Predicate.isEqual(null).test(null);//true
    Predicate.isEqual(null).test("String");//false
    Predicate.isEqual("String").test(null)//false
    
  • not静态方法用于根据传入的Predicate实例创造‘否命题’,并返回新的Predicate实例

(4)Function接口

  • Function接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件

    @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;
        }
    }
    
  • apply方法用于将传入的对象,转换为其他类型的对象

    Function<Integer,String> integerToString = (integer)->"String:"+String.valueOf(integer);
    System.out.println(integerToString.apply(256));//String:256
    
  • andThen方法将当前接口实例与传入的Function接口组合操作

    Function<Integer,String> integerToString = (integer)->String.valueOf(integer);
    Function<String,Double> stringToDouble = (str)->Double.valueOf(str);
    System.out.println(integerToString.andThen(stringToDouble).apply(1));//1.0
    
  • compose方法与andThen相反,用于先用传入的Function接口进行转换再使用本接口进行转换操作

    System.out.println(stringToDouble.compose(integerToString).apply(1));
    
发布了19 篇原创文章 · 获赞 6 · 访问量 728

猜你喜欢

转载自blog.csdn.net/qq_42631607/article/details/105363222