匿名内部类&Lambda表达式&函数式接口

简介: 匿名内部类&Lambda表达式&函数式接口

一. 匿名内部类

匿名内部类作用:简化代码

把子类继承父类/实现接口,重写父类/接口中的方法,创建子类对象合一步完成

匿名内部类的最终产物:

子类/实现类对象

格式:

在匿名内部类中,接口也是可以new出对象的

new 父类/接口(){

从写重复父类/接口中的方法

};

例如1:

/*
线程的父类是Thread,原来的写法:
    1.先创建MyThread类继承自Thread
    2.使用的时候再new MyThread,然后再调用start方法
 */
new MyThread().start();
--------------------------------------------------------------------
/*
使用匿名内部类的写法:
 */
new Thread(){
    //重写run方法,设置线程任务
    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+"黑马");
        }
    }
}.start();

例如2:

/*
不使用用匿名内部类写法:
    1.先创建RunnableImpl类,并实现Runnable接口
    2.再new RunnableImpl
    3.将任务交给Thread,再调用start()
 */
Runnable r = new RunnableImpl();
new Thread(r).start();
--------------------------------------------------------------------
/*
使用匿名内部类写法:
 */
new Thread(new Runnable(){
    //重写run方法,设置线程任务
    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            System.out.println("朱上林"+i);
        }
    }
}).start();

二. Lambda表达式

Lambda表达式,用来简化匿名内部类的书写,不再有“不得不创建接口对象”的束缚,不再有“抽象方法重写”的负担,就是这么简单!

用法

定义方法:

定义方法invokeCook(Cook cook)中有接口参数,并且方法体中调用了该接口参数的抽象方法cook.makeFood()

调用方法:

invokeCook(()->{}); 此时Lambda表达式表示的就是cook.makeFood()抽象方法的实现(相当于回调函数,调用的时候实现抽象方法)

案例1:无参数的Lambda

//创建Cook接口,并定义抽象方法
public interface Cook {
    //定义无参数无返回值的方法makeFood
    public abstract void makeFood();
}
//测试类
public class Demo01Cook {
    public static void main(String[] args) {
        //使用匿名内部类写法
        invokeCook(new Cook() {
            @Override
            public void makeFood() { 
                System.out.println("吃饭了");
            }
        });
        //使用Lambda表达式,简化匿名内部类的书写
        invokeCook(()->{ //Lambda表达式来表示实现接口中的抽象方法
            System.out.println("吃饭了");
        });
        //优化Lambda,如果写在一行{} return ; 三个同时省略
        invokeCook(()-> System.out.println("吃饭了"));
    }
    
    public static void invokeCook(Cook cook){
        cook.makeFood();
    }
}

案例2:有参数的Lambda

//对数组中的Person对象使用Arrays的sort方法通过年龄进行升序(前边-后边)排序,匿名内部类方式实现
Arrays.sort(arr, new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
        return o1.getAge()-o2.getAge();
    }
});
//使用Lambda表达式,简化匿名内部类
Arrays.sort(arr,(Person o1, Person o2)->{
    return o1.getAge()-o2.getAge();
});
//简化Lambda,省略{}  return ;
Arrays.sort(arr,(Person o1, Person o2)->o1.getAge()-o2.getAge()
);

案例3

public interface Calculator {
    //定义一个计算两个int整数和的方法并返回结果
    public abstract int calc(int a,int b);
}
public class Demo01Calculator {
    public static void main(String[] args) {
        //调用invokeCalc方法,方法的参数是一个接口,使用匿名内部类
        invokeCalc(10, 20, new Calculator() {
            @Override
            public int calc(int a, int b) {
                return a+b;
            }
        });
        //使用Lambda表达式简化匿名内部类的书写
        invokeCalc(120,130,(int a,int b)->{
            return a + b;
        });
        //优化省略Lambda
        invokeCalc(120,130,(a,b)-> a + b);
    }
    public static void invokeCalc(int a,int b,Calculator c){
        int sum = c.calc(a,b);
        System.out.println(sum);
    }
}

Lambda使用前提

1.使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。 无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。

2.使用Lambda必须具有上下文推断。 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

三、函数式接口

概念

有且只有一个抽象方法的接口,称之为函数式接口

当然接口中可以包含其他的方法(默认,静态,私有)

@FunctionalInterface注解

作用:可以检测接口是否是一个函数式接口,类似@Override检查一个重写方法

是:编译成功

否:编译失败(接口中没有抽象方法,或者抽象方法的个数多余1个)

例:

定义函数式接口

@FunctionalInterface
public interface MyFunctionalInterface {
    //定义一个抽象方法
    public abstract void method();
}

使用函数式接口

public class Demo {
    //定义一个方法,参数使用函数式接口MyFunctionalInterface
    public static void show(MyFunctionalInterface myInter){
        myInter.method();
    }
    public static void main(String[] args) {
        //调用show方法,方法的参数是一个接口,所以可以传递接口的实现类对象
        show(new MyFunctionalInterfaceImpl());
        //调用show方法,方法的参数是一个接口,所以我们可以传递接口的匿名内部类
        show(new MyFunctionalInterface() {
            @Override
            public void method() {
                System.out.println("使用匿名内部类重写接口中的抽象方法");
            }
        });
        //调用show方法,方法的参数是一个函数式接口,所以我们可以Lambda表达式
        show(()->{
            System.out.println("使用Lambda表达式重写接口中的抽象方法");
        });
        //简化Lambda表达式
        show(()-> System.out.println("使用Lambda表达式重写接口中的抽象方法"));
    }
}

四、函数式编程

Lambda的特点: 延迟加载,提升性能

Lambda的使用前提,必须存在函数式接口,函数式接口作为形参的方法,在被调用时,实参Lambda表达式就表示该函数式接口中的那个抽象方法的实现。

例1

发现以下代码存在的一些性能浪费的问题

调用showLog方法,传递的第二个参数是一个拼接后的字符串

先把字符串拼接好,然后在调用showLog方法,showLog方法中如果传递的日志等级不是1级

那么就不会是如此拼接后的字符串,所以感觉字符串就白拼接了,存在了浪费

public class Demo01Logger {
    public static void showLog(int level, String message){
        if(level==1){
            System.out.println(message);
        }
    }
    public static void main(String[] args) {
        String msg1 = "Hello";
        String msg2 = "World";
        String msg3 = "Java";
        showLog(2,msg1+msg2+msg3);
    }
}

使用Lambda延迟加载,提升性能:

1.将新能浪费的代码定义为一个函数式接口中的一个抽象方法

@FunctionalInterface
public interface MessageBuilder {
    //定义一个拼接消息的抽象方法,返回被拼接的消息
    public abstract String builderMessage();
}

2.方法中将接口作为参数,调用该方法时,lambda表达式会根据判断是否成立,决定是否执行,所以可以把Lambda表达式看作,函数式接口中的那个抽象方法的实现

public class Demo02Lambda {
    public static void showLog(int level, MessageBuilder mb){
        //对日志的等级进行判断,如果是1级,则调用MessageBuilder接口中的builderMessage方法
        if(level==1){
            System.out.println(mb.builderMessage());
        }
    }
    public static void main(String[] args) {
        String msg1 = "Hello";
        String msg2 = "World";
        String msg3 = "Java";
        showLog(1,()->{
            return  msg1+msg2+msg3;
        });
    }
}

例2:参数为函数式接口

public class Demo01Runnable {
    //函数式接口作为形参的方法,再被调用时,实参lambda表达式就表示该函数式接口中的那个抽象方法的实现。
    public static void startThread(Runnable run){
          new Thread(run).start();
    }
    public static void main(String[] args) {
        startThread(()->{
            System.out.println("线程启动了");
        });
    }
}

例3:方法的返回值类型是函数式接口

public class Demo02Comparator {
    //定义一个方法,方法的返回值类型使用函数式接口Comparator
    public static Comparator<String> getComparator(){
        //方法的返回值类型是一个接口,那么我们可以返回这个接口的匿名内部类
        /*return new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //按照字符串的降序排序
                return o2.length()-o1.length();
            }
        };*/
        //使用Lambda表达式
        return (o1, o2)->o2.length()-o1.length();
    }
    public static void main(String[] args) {
        //创建一个字符串数组
        String[] arr = {"aaa","b","cccccc","dddddddddddd"};
        //调用Arrays中的sort方法,对字符串数组进行排序
        Arrays.sort(arr,getComparator());
        //输出排序后的数组  
        System.out.println(Arrays.toString(arr)); 
  //[dddddddddddd, cccccc, aaa, b]  
   }
}
目录
相关文章
|
SQL 关系型数据库 MySQL
mysql的binlog恢复数据
mysql的binlog恢复数据
266 0
CountDownLatch实现原理全面解析
CountDownLatch是一个同步工具类,用来协调多个线程之间的同步(即:用于线程之间的通信而不是互斥)。它允许一个或多个线程进入等待状态,直到其他线程执行完毕后,这些等待的线程才继续执行。
|
SQL 存储 大数据
Presto全网最佳学习资料汇总
Presto这几年在国内使用的越来越广泛,成为企业中必备的Adhoc/BI报表/轻量级ETL引擎,国内公司比如阿里巴巴、滴滴、头条、京东、小米都有几百数上千台的Presto集群,这主要还是由Presto优秀的特性使然(下文详细说明)。为了方便咱们中国的Presto使用者、爱好者学习使用Presto,我把Presto周边的资料做了一个搜集整理,方便大家查阅。
Presto全网最佳学习资料汇总
|
11月前
|
人工智能 自然语言处理 测试技术
DeepSeek V3:DeepSeek 开源的最新多模态 AI 模型,编程能力超越Claude,生成速度提升至 60 TPS
DeepSeek V3 是深度求索公司开源的最新 AI 模型,采用混合专家架构,具备强大的编程和多语言处理能力,性能超越多个竞争对手。
1852 5
DeepSeek V3:DeepSeek 开源的最新多模态 AI 模型,编程能力超越Claude,生成速度提升至 60 TPS
|
数据库连接 网络安全 数据库
Could not create connection to database server.Attempted reconnect 3 times. Giving up.
这篇文章提供了解决数据库连接问题的方法,建议在连接字符串后添加`?serverTimezone=UTC`来指定时区,并检查网络设置、连接属性、驱动版本以及是否需要SSH或SSL连接。
Could not create connection to database server.Attempted reconnect 3 times. Giving up.
|
SQL 关系型数据库 Go
Golang ORM框架介绍及比较
Golang ORM框架介绍及比较
|
数据可视化
第七章:MATLAB基本运算符号解析及案例详解
第七章:MATLAB基本运算符号解析及案例详解
432 1
|
JavaScript 前端开发
脚手架vue-cli自定义创建Vue项目,完整详细步骤!
脚手架vue-cli自定义创建Vue项目,完整详细步骤!
|
存储 关系型数据库 MySQL
PolarDB产品使用问题之如何进行表分区
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
348 0
端口被占用 --- 解决方案
端口被占用 --- 解决方案
284 0