Lambda-让人又爱又恨的“->"

2021-11-01 11:15:29 浏览数 (1)

写在前边

  • 聊到Java8新特性,你第一反应想到的肯定是Lambda表达式和函数式接口的出现。要说他到底有没有在一定程度上“优化”了代码的简洁性呢?抑或是ta在一定程度上给程序员增加了阅读和debug的难度,让不少程序员头疼。这期来接着“聊聊Java”,新特性篇只又爱又恨的Lambda

Lambda表达式

实质属于函数式编程的概念,可返回一个接口的实现

线程中的应用

传统方式

创建一个一次性的类
代码语言:javascript复制
//一次性的类,用在new Thread中充当Runnable对的实现类
class runnable implements Runnable{

    @Override
    public void run() {
        System.out.println("我在路上");
    }
}

public class lambdaTest {
    public static void main(String[] args) {
        runnable runnable = new runnable();
        Thread thread1 = new Thread(runnable);
    }
}
复制代码
(稍微优化)匿名内部类
代码语言:javascript复制
Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我在路上");
            }
        });
复制代码

作用

  • 避免匿名内部类定义过多
  • 使代码看起来简洁
  • 简化代码,只留下核心逻辑

函数式接口

定义:任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口

代码语言:javascript复制
public interface Runnable{
    void run();
}
复制代码

为了避免后来人给这个接口添加函数后,导致该接口有多个函数,不再是函数式接口,我们可以在接口类的上方声明**@FunctionalInterface**

所有的Lambda的类型都是一个接口

  • 而Lambda表达式本身,就是这个接口的实现

如果定义成实现类,就会报错

  • 我们在程序编译的时候并不知道我们最终创建出来的对象具体是哪个子类,直到运行时期才能得知,随之我们可以让这个like指向指向各种不同的类上,可以调用各种不同的子类方法,大大提高了程序的可扩展性

而这里我们用lambda实际上是等价于匿名内部类(没有类名),实际创建出来的类是什么,我们不知道,所以我们会定义成接口,利用多态的向上转型特性 关于多态的更多特性,在我的另一篇博客中 : 传送门->

方法引用

Demo

代码语言:javascript复制
 //接口定义
    interface parseIntNum{
        //定义一个String转化成Integer的方法
        int pass(String s);
    }
    public static void main(String[] args) {
        parseIntNum parseIntNum1;
        parseIntNum parseIntNum2;
        //原始lambda
        parseIntNum1 = (str)-> Integer.parseInt(str);
        System.out.println(parseIntNum1.pass("1"));
        //方法引用改进版本
        parseIntNum2 = Integer::parseInt;
        System.out.println(parseIntNum2.pass("1"));
    }
复制代码

所谓方法引用,是指如果某个已经存在的方法,他的签名接口里边定义的函数恰好一致,就可以直接传入方法引用。 因为parseIntNum接口定义的方法是int pass(String s),和Integer中的静态方法int parseInt(String s)相比,除了方法名外,方法参数一致,返回类型相同,这就是我们说的方法签名一致,可以直接传入方法引用

方法引用的写法

  • 其实很简单,只需要使用操作符双冒号** "::"**

常见的方法引用

常见的引用形式

类名::静态方法名

调用类的静态方法

  • 其实我们上边使用Integer::parseInt 就等价于调用 Integer的静态方法 parseInt

对象:实例方法

此处小小中二了一点hhhh

代码语言:javascript复制
class Naruto{
    public static void Rasengan(){
        System.out.println("螺旋丸");
    }
}
//接口定义
interface Ninja{
    //定义一个奥义方法
    void aoyi();
}
public class methodQuote {
    //通过引用Naurto的螺旋丸
    Ninja ninja=Naruto::Rasengan;
    //再发动奥义
    ninjia.aoyi();
}
复制代码

数据类型:new

代码语言:javascript复制
 public static void main(String[] args) {
     //方式一
        IntFunction<int []> arr1 = new IntFunction<int[]>() {
            @Override
            public int[] apply(int num) {
                return new int[num];
            }
        };
        arr1.apply(10);
     //方式二(方法引用)
        IntFunction<int []> arr2 = int[]::new;
        arr2.apply(10);

    }
复制代码
  • 除此之外还有很多种形式,这里就不过多赘述了

开发常用

常用小技巧:遍历数组打印

对比三种方法,可以看出简洁程度

代码语言:javascript复制
ArrayList<Integer> arrayList=new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
//匿名内部类
        arrayList.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
//lambda优化
		arrayList.forEach((integer)-> System.out.println(integer));	
//方法引用打印,用方法引用替代了我们的匿名内部类(相当于替代了lambda)
        arrayList.forEach(System.out::println);
复制代码

疑问解决

一开始有个疑问,看起来方法引用省略了参数,那我们Intger.parseInt是去对谁操作?

  • 其实是自己混淆了lambda,lambda定义的时候那个参数,根本不是实际的参数

可以说那个参数,只是为方法体服务的,只是方法体里边会用到. 而我们都用了方法引用了,前提就是参数和返回值一样,方法体也是我们想要实现的内容,这时自然而然都不用我们写方法体了,那方法体所依赖的参数也自然不用派上用场了

写在最后

  • 最近要开始忙活项目了,对于这些基础知识的更深入掌握,也许会放在以后准备面试的时候,目前理解不深,如有错误之处还望指出!

写在最后

  • 本专栏还会断断续续更新一些Java基础知识和面经,提前为以后面试打下基础!

0 人点赞