匿名内部类&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]  
	 }
}
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Sunny_yiyi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值