匿名内部类&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]  
   }
}
目录
相关文章
|
编译器
C++11 lambda表达式(上)
C++11 lambda表达式
65 0
|
2月前
|
安全 Java Serverless
Lambda表达式使用及详解
Java 8引入的Lambda表达式是一种重要的功能,它允许你以更简洁、更直接的方式传递方法。Lambda可以被用来代替只有单个抽象方法的接口的匿名实现类。这里有一些Lambda表达式的实际应用场景及其示例:
|
5月前
|
算法 编译器 C++
C++一分钟之—Lambda表达式初探
【6月更文挑战第22天】C++的Lambda表达式是匿名函数的快捷方式,增强函数式编程能力。基本语法:`[capture](params) -&gt; ret_type { body }`。例如,简单的加法lambda:`[](int a, int b) { return a + b; }`。Lambda可用于捕获外部变量(值/引用),作为函数参数,如在`std::sort`中定制比较。注意点包括正确使用捕获列表、`mutable`关键字和返回类型推导。通过实践和理解这些概念,可以写出更简洁高效的C++代码。
52 13
|
6月前
|
Java 编译器
Lambda表达式
Lambda表达式
31 0
|
6月前
|
编译器 C语言 C++
C++ lambda表达式
C++ lambda表达式
|
6月前
|
编译器 C++
C++lambda表达式
C++lambda表达式
|
6月前
|
算法 编译器
C++11(lambda表达式)
C++11(lambda表达式)
62 0
|
Java 编译器
函数式接口
函数式接口
|
Java 开发者
1.4 Lambda表达式的基础:Lambda表达式与匿名类的对比
1.4 Lambda表达式的基础:Lambda表达式与匿名类的对比
91 0
|
Java 编译器
1.2 Lambda表达式的基础:Lambda表达式的函数式接口
1.2 Lambda表达式的基础:Lambda表达式的函数式接口
63 0