Java中lambda表达式的使用

2023-12-13 16:51:12

???💕"我不要麻木的死去"💕

作者:Mylvzi?

?文章主要内容:Java中lambda表达式的使用?

一.背景

? lambda表达式是Java SE 8中一个重要的新特性,允许你使用一个表达式来代替功能接口。lambda表达式可以看作一个没有返回值,没有修饰符修饰的方法,它具有参数和这些参数的主体(方法体)Lambda 表达式(Lambda expression),基于数学中的λ演算得名,也可称为闭包(Closure)

二.语法

1.基本语法

??基本语法: (parameters) -> expression 或 (parameters) ->{ statements};

  1. parameters:类似于方法中的形参列表,参数来源于函数式接口的参数
  2. ->:可以理解为"被用于"
  3. expression :方法体? 可以是一条语句也可以是多条语句

2.函数式接口

? 函数式接口就是只含有一条抽象方法的接口

如果接口上声明了@FunctionalInterface,那么编译器就会按照函数式接口进行检验,也即此接口内部只能有一个抽象方法,但是可以使用default来添加一个非抽象方法

@FunctionalInterface
interface FuncInterface1 {
    void test();
}

// 这样也是可以的
@FunctionalInterface
interface FuncInterface2 {
    void test();
    default void test2(){
        System.out.println("===");
    }
}

三.lambda表达式的基本使用

?lambda表达式就相当于使用一种更简单的方式重写了抽象方法(相比于使用匿名内部类)

以下所有情况的实现方式都有两种,lambda表达式和匿名内部类?

1.无返回值,无参数

//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}

使用两种方式实现

        // 使用lambda表达式重写函数式接口的抽象方法     参数       方法体
        NoParameterNoReturn noParameterNoReturn1 = () -> System.out.println("hello1");
        noParameterNoReturn1.test();// 输出hello1

        // 使用匿名内部类重写函数式接口的抽象方法
        NoParameterNoReturn noParameterNoReturn2 = new NoParameterNoReturn() {
            @Override
            public void test() {
                System.out.println("hello2");
            }
        };

2.无返回值,一个参数

//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
    void test(int a);
}

使用两种方式实现

        // 使用lambda表达式
        OneParameterNoReturn oneParameterNoReturn = (x) -> System.out.println(x);
        oneParameterNoReturn.test(10);// 打印10
        
        // 使用匿名内部类
        OneParameterNoReturn oneParameterNoReturn1 = new OneParameterNoReturn() {
            @Override
            public void test(int a) {
                System.out.println(a);
            }
        };

注意:当参数只有一个或者方法体只有一句时,可以省略外面的();?

3.无返回值多个参数?

//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
    void test(int a,int b);
}

使用两种方式实现

        // 使用lambda表达式
        MoreParameterNoReturn moreParameterNoReturn = (x,y) -> System.out.println(x+y);
        moreParameterNoReturn.test(10,20);// 输出30
        
        // 使用匿名内部类
        MoreParameterNoReturn moreParameterNoReturn1 = new MoreParameterNoReturn() {
            @Override
            public void test(int a, int b) {
                System.out.println(a+b);
            }
        };

4.有返回值无参数

//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
    int test();
}

使用两种方式实现

        NoParameterReturn noParameterReturn = () -> {return 10;};
        System.out.println(noParameterReturn.test());// 输出10

        // 简化
        NoParameterReturn noParameterReturn1 = () ->10;
        System.out.println(noParameterReturn1.test());// 输出10
        
        // 使用匿名内部类
        NoParameterReturn noParameterReturn2 = new NoParameterReturn() {
            @Override
            public int test() {
                return 10;
            }
        };

5.有返回值一个参数

//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
    int test(int a);
}

使用两种方式实现

        OneParameterReturn oneParameterReturn = (x) -> {return x*x;};
        System.out.println(oneParameterReturn.test(10));// 输出100

        // 简化
        OneParameterReturn oneParameterReturn1 = x -> x*x;
        System.out.println(oneParameterReturn1.test(10));// 输出100

        // 使用匿名内部类
        OneParameterReturn oneParameterReturn2 = new OneParameterReturn() {
            @Override
            public int test(int a) {
                return a*a;
            }
        };

6.有返回值多参数

@FunctionalInterface
interface MoreParameterReturn {
    int test(int a,int b);
}

使用两种方式实现

        MoreParameterReturn moreParameterReturn = (x,y) -> {return x+y;};
        System.out.println(moreParameterReturn.test(10, 20));// 输出30
        
        // 使用匿名内部类
        MoreParameterReturn moreParameterReturn1 = new MoreParameterReturn() {
            @Override
            public int test(int a, int b) {
                return a+b;
            }
        };

以上就是lambda表达式的基本使用?

四.变量捕获

? 1.什么是变量捕获

? 变量捕获:在一个内部作用域内(lambda表达式,匿名内部类等)中引用外部作用域(方法,类)的变量?

? 2.捕获的方式

? 当在内部作用域内引用外部作用域的变量时,Java会在内部作用域内对引用的变量进行拷贝/引用

? 3.匿名内部类的变量捕获

? 匿名内部类就是没有名字的类,往往用于接口的实现,下面使用一个简单的匿名内部类来实现

class Demo {
    public void func() {
        System.out.println("hello");
    }
}
public class Demo10 {
    public static void main(String[] args) {
        new Demo() {
            @Override
            public void func() {
                System.out.println("hello Demo");
            }
        };
    }
}

?如果在匿名内部类中进行变量的捕获

1.局部变量? 且没有被修改过

2.局部变量? 但是被修改

可以发现,如果对A进行修改就会报错

这也是变量捕获的一个要求,即:

在内部作用域内捕获的变量(使用的变量)只能是被final修饰的或者从来没有被修改过的(被final修饰就代表无法改变)

? 如果你传入的变量并没有被final修饰,则系统会强制要求你传入的变量不能被修改!!!?

?4.lambda表达式中的变量捕获

? lambda表达式可以看作对匿名内部类进行的语法上的精简,同时也可以进行变量的捕获

@FunctionalInterface
interface DemoTest {
    void func();
}

当使用被修改过的变量 -->依然报错

尽量使用被final修饰的变量

引用类变量

@FunctionalInterface
interface DemoTest {
    void func();
}
public class Demo10 {
    // 使用类变量  可以直接访问
    public static int a = 10;
    public static void main(String[] args) {
        // lambda表达式
        DemoTest demoTest = () -> {
            System.out.println(a);
        };

    }

对于类变量来说,在lambda表达式中可以直接引用,因为类变量是属于类的,是所有对象公用的,它本身就有"静态"的属性?

五.在优先级队列中的使用

? 优先级队列中往往涉及到比较,我们在实例化优先级队列的时候往往要传递一个比较器,规定比较的对象

        // 使用匿名内部类实现Comparator接口
        PriorityQueue<Integer> priorityQueue = new PriorityQueue(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return  o2 - o1;
            }
        });

?其实也能看出,Comparator方法应该是一个函数式接口,它属于"多个参数,有返回值"的类型(对应于上面的最后一种情况),此处就可以使用lambda表达式进行语法上的精简

        // 使用lambda表达式实现优先级队列中的比较
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>((o1,o2) ->{return o1-o2;});

Comparator的源码 -->多个参数,有返回值的接口

六.创建线程

?创建线程的方式有很多种,可以采用匿名内部类,lambda表达式

1.匿名内部类创建线程

        // 创建线程 ->使用匿名内部类
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                System.out.println("hello thread");
            }
        };
        
        // 这种方式可以降低耦合性(推荐)
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello thread2");
            }
        });

为什么可以使用匿名内部类创建一个线程呢?主要是因为Thread类实现了Runnable接口,这个接口是一个函数式接口,只有一个抽象方法run()

2.使用lambda表达式

? 既然Thread可以通过匿名内部类方式创建线程,同时就可以使用lambda表达式来简化创建

        // 方式1
        Thread thread1 = new Thread(() ->{
            System.out.println("hello thread1");
        });
        thread1.run();// 输出hello thread1

        //方式2  利用lambda表达式实例化一个runnable接口  降低耦合性
        Runnable runnable = () -> System.out.println("hello thread2");
        Thread thread2 = new Thread(runnable);
        thread2.run();// 输出hello thread2

七.lambda表达式在集合类中的使用(以后最常用的一种)

?为了能够让Lambda和Java的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与Lambda表达式对 接。

1.Collection接口

1.1forEach()

1.源码

forEach()在Iterable接口之下,源码如下:

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

对传入的集合的每个元素执行action操作,进一步观察

可以观察到。forEach()方法接受了一个Comsumer类型的参数action,接着会对每一个action执行对应的操作。Consumer是一个函数式接口,属于"一个参数但无返回值"的类型,内部有一个抽象方法accept用于接受参数并执行特定的操作

2.使用匿名内部类实现
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("my");
        list.add("friends");
        list.add("!!!");

        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.print(s + " ");
            }
        });

?

3.使用lambda表达式
        // lambda表达式
        list.forEach((s) -> System.out.print(s + " "));

1.2removeif()

? removeif()用于根据条件删除集合中的对应数据,如果有对应条件的数据,删除,并将标志位removed设置为true(注意:removeif不是只删除一个符合条件的数据,而是类似于removeAll一样的效果)

1.源码:
    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }
2.图解:

3.使用
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("my");
        list.add("friends");
        list.add("!!!");

        // 1.先打印原数据
        list.forEach((s) -> System.out.print(s + " "));

        // 2.删除长度 <= 3的字符串
        boolean flg = list.removeIf(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.length() <= 3;
            }
        });

        System.out.println("");

        // 3.判断是否有删除操作
        System.out.println(flg);

        // 4.打印删除之后的数据
        list.forEach((s) -> System.out.print(s + " "));

打印结果:

?2.List接口

2.1 sort

1.源码:
    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }
?2.图解:

3.使用:
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("my");
        list.add("friends");
        list.add("!!!");

        // 使用匿名内部类
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);// 从小到大排序
            }
        });

        // 使用lambda表达式
        list.sort((o1,o2) -> {
            return o1.compareTo(o2);
        });

        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.print(s + " ");
            }
        });

打印结果:

2.2 reolaceAll?

?1.源码:
    default void replaceAll(UnaryOperator<E> operator) {
        Objects.requireNonNull(operator);
        final ListIterator<E> li = this.listIterator();
        while (li.hasNext()) {
            li.set(operator.apply(li.next()));
        }
    }
2.图解:

3.使用:
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);

        // 打印原数据
        System.out.println(list.toString());

        // 使用匿名内部类
        list.replaceAll(new UnaryOperator<Integer>() {
            @Override
            public Integer apply(Integer integer) {
                return integer*2;// 将所有的值都设置为原来的两倍
            }
        });

        // 使用lambda表达式
        list.replaceAll((x) ->{return x*2;});

        // 打印修改之后的数据
        System.out.println(list.toString());

打印数据:

3.Map接口

3.1?forEach

1.源码:
default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }
2.图解:

3.使用:
        Map<String,Integer> map = new HashMap<>();
        map.put("zs",1);
        map.put("ls",2);
        map.put("ww",3);
        // 使用匿名内部类
        map.forEach(new BiConsumer<String, Integer>() {
            @Override
            public void accept(String s, Integer integer) {
                System.out.println("key:" + s + " val:" + integer);
            }
        });
        System.out.println("==============");
        // 使用lambda表达式
        map.forEach((key,val) ->{
            System.out.println("key:" + key + " val:" + val);
        });

打印结果:

文章来源:https://blog.csdn.net/Mylvzi/article/details/134854020
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。