Lambda表达式

引言:

在面向过程程序语言中,参数的传递是基本类型的变量;

在面向对象语言中,参数的传递可以是基本类型的变量或者对象变量;

在java8中又新增了传递类型,传递参数可以是方法或者代码块,即以匿名函数的形式传递。

使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口的临时实现。

首先我们要先了解什么是函数式接口:

  ①是一个接口,复合Java接口的定义

  ②只包含一个抽象方法的接口

  ③有且仅有一个待实现的方法,所以lambda可以自动填充上这个尚未实现的方法

  ④可以包括其他的default方法、static方法、private方法

  ⑤使用@FunctionalInterface注解可以检测是否符合函数式接口定义

一、自定义Lambda表达式:

形式一:(参数)+箭头+一个表达式

原本需要单独定义的一个排序类,现在可以作为一个匿名函数直接传入到Arrays.sort([ ],comparator)中

import java.util.Comparator;
public class MyComparator implements Comparator<String> {
    @Override
    public int compare(String o1, String o2) {
        int len1=(o1==null?0:o1.length());
        int len2=(o2==null?0:o2.length());
        return len1-len2;
    }
}
public static void main(String[] args) {
        String[] planets=new String[]{"mercury","tom","mars","plane","xjy","zq"};
//        Arrays.sort(planets,new MyComparator());
        Arrays.sort(planets,(first,second)->(-1)*(first.length()-second.length()));//改写为lambda表达式
        System.out.println(Arrays.toString(planets));
    }

形式二:(参数)+箭头+{多条语句}

形式一箭头后的表达式也可以拆分成两句,但必须要加一对{ }

 Arrays.sort(planets,(first,second)->{int result=(-1)*(first.length()-second.length());return result;});

形式三(参数列表无参数):( )+箭头+{表达式}

就拿最经典的创建一个线程来说,run()中无参数,用一对()表示

public class ThreadDemo {
    public static void main(String[] args) {
// 普通写法
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                int sum=0;
//                for (int i=1;i<=100;i++){
//                    sum=sum + i;
//                }
//                System.out.println("总和"+sum);
//            }
//        }).start();

// lambda表达式写法
        new Thread(()->{
                int sum=0;
                for (int i=1;i<=100;i++){
                    sum=sum + i;
                }
                System.out.println("总和"+sum);}).start();
    }
}

形式四(一个参数,可省略括号):参数+箭头+{表达式}

这里我们定义一个接口

public interface Adder {
    int selfAdd(int x);
}

接口实现

public class AdderImpl {
        //lambda表达式写法
        Adder c2=x->{
            if (x>0)
                return x+1;
            return 0;
        };
    }


    class AdderImpl2 implements Adder{
        //普通写法
        @Override
        public int selfAdd(int x) {
            if (x>0)
                return x+1;
            return 0;
        }
    }

自定义Lambda表达式总结:

1、类似于匿名方法,一个没有名字的方法

2、可以忽略参数类型(我写的所有形式都忽略了参数类型)

3、不声明返回值类型

4、没有public/protected/private/static/final等修饰符

5、单句表达式,将直接返回值,不用写大括号

6、带return语句,算多句表达式,必须用大括号{ }

二、Lambda表达式的方法引用:

如果采用大量重复性的函数式接口,会使源码过于膨胀,维护效率过低,并且自定义的lambda表达式也不便于理解,

这个时候我们就需要一种简介明了的形式来表示lambda表达式,这就是方法引用,它允许lambda表达式传递现有的类库函数。

先介绍几种常用的Java自带的函数式接口:

接口参数返回值
Predicate<T>TBoolean
Consumer<T>Tvoid
Function<T,R>TR
Supplier<T>NoneT

使用案例:

import org.junit.Test;
import java.util.*;
import static java.lang.Math.floor;
import static java.lang.Math.random;

public class FunctionalInterfaceTest {
    private String[] planets =new String[]{"xyy","xx","yxyx","x"};
    @Test
    public void consumerInterface(){
        Consumer<String> printer = s -> System.out.println("name :"+s);
        for (String p:planets){
            printer.accept(p);
        }
    }
    @Test
    public void predicateInterface() {
        Predicate<String> evenLength = s -> {
            return s.length()%2==0?true:false;
        };
        for (String p:planets){
            if (evenLength.test(p)){
                System.out.println(p);
            }
        }
    }

    @Test
    public void SupplierInterface() {
        Supplier<String> planetFactory = ()->planets[(int) floor(random()*4)];
        for (int i=0;i<4;i++){
            System.out.println(planetFactory.get());
        }
    }

    @Test
    public void FunctionInterface() {
        Function<String,String> upper =s -> {
            return  s.toUpperCase();
        };
        for (String p:planets){
            System.out.println(upper.apply(p));
        }
    }
}

为什么要介绍自带得函数式接口呢,就是为了避免大量的自定义函数式接口让代码显得冗余。

方法引用的几种形式:

形式一:class::staticMethod    例:Math::abs

public interface NumFunction {
    double calculate(double num);
}
public class ClassStaticMethod {
    public static double worker(NumFunction nf,double num){
        return nf.calculate(num);
    }

    public static void main(String[] args) {
        double a=-5.3;
        double b=worker(Math::abs,a);  //Math中的abs方法会自动变成NumFunction nf接口的实现,此时calculate(num)方法变成Math.abs(num)
        System.out.println(b);
        double c=worker(Math::floor,a);
        System.out.println(c);
    }
}

形式二:class::instanceMethod    例:String::compareToIgnoreCase

import java.util.Arrays;


public class ClassInstanceMethod {
    public static void main(String[] args) {
        String[] plannets= new String[]{"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};
        Arrays.sort(plannets,String::compareToIgnoreCase);    //这里String::compareToIgnoreCase等价与(x,y)->x.compareToIgnoreCase(y)
        System.out.println(Arrays.toString(plannets));
    }

}

形式三:object::instanceMethod    例:System.out::println

public interface PrintFunction {
    void exec(String s);
}
public class ObjectInstanceMethod {
    public static void worker(PrintFunction pf,String a){
        pf.exec(a);
    }

    public static void main(String[] args) {
        String a="xjy";
//        worker(x->System.out.println(x),a);
        worker(System.out::println,a);
    }
}

 

此外,还支持this::instanceMethod调用或者super::instanceMethod调用

public class ThisInstanceMethod extends Father{
    public static void main(String[] args) {
        new ThisInstanceMethod().test();
    }
    public void test(){
        String[] plannets= new String[]{"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};
//        Arrays.sort(plannets,this::lengthCompare);
        Arrays.sort(plannets,super::lengthCompare);
        System.out.println(Arrays.toString(plannets));

    }
    public  int lengthCompare(String first,String second){
        return first.length()-second.length() ;
    }
}
class Father{
    public int lengthCompare(String first,String second){
        return first.length()-second.length();
    }
}

形式四:class::new / class[ ]::new 调用某类的构造函数,创建某类的单个对象或对象数组。

import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;

public class Person {
    private String name;
    private int age;

    public Person() {
        this.name="tom";
        this.age=18;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) {
//        Supplier<Person> s=()->new Person();
        Supplier<Person> s=Person::new;
        Person person = s.get();
        System.out.println(person.getName());
        IntFunction<int[]> intArray=int[]::new;
        int[] nums=intArray.apply(10);
        Function<Integer,Person[]> personArray=Person[]::new;
        Person[] persons= personArray.apply(5);
    }
}

使用lambda表达式的注意点与建议:

①再重载调用时,系统会自动依据重载的规则和参数类型推理调用合适的接口。

②lambda表达式和匿名内部类/局部内部类一样,可以共享外部一层嵌套块的变量,但是变量必须是final或者不会被改变的非final对象。

public class LambdaScopeTest {
    int x=0;

    class FirstLevel{
        int x=1;
        void methodInFirstLevel(int x){
            Consumer<Integer> myConsumer=(y)->{
                System.out.println("x="+x);
                System.out.println("y="+y);
                System.out.println("this.x="+this.x);
                System.out.println("lambdaScopeTest.this.x="+LambdaScopeTest.this.x);
            };
            myConsumer.accept(x);
        }
    }

    public static void main(String[] args) {
        LambdaScopeTest lst=new LambdaScopeTest();
        FirstLevel fl=lst.new FirstLevel();
        fl.methodInFirstLevel(22);
//        List<String> list=new ArrayList<>();
//        for (int i=0;i<10;i++){
//            list.add("xjy"+i);
//        }
//        list.forEach(System.out::println);
    }
}

③lambda表达式不可以声明与外部嵌套块同名的参数或局部变量。

④lambda比嵌套类优先级高

⑤方法引用比自定义lambda表达式的优先级高

⑥应坚持使用标准的函数式接口

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pixie:)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值