Lambda、方法引用、函数式接口

简介: Lambda、方法引用、函数式接口

1.函数式编程思想概述

面向对象思想:

​ 做一件事情,找一个能够解决这个事情的对象,调用对象的方法来完成这件事情

函数式编程思想:

​ 重视的是结果,怎么做事情,不重视完成的过程,找谁来做

2.使用Lambda表达式简化匿名内部类

/*
    使用Lambda表达式简化匿名内部类(重点)
 */
public class Demo01Lambda {
    public static void main(String[] args) {
        //使用匿名内部类的方式创建一个新的线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"-->使用匿名内部类的方式,实现多线程程序!");
            }
        }).start();

        //使用Lambda表达式的方式创建一个新的线程
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"-->使用Lambda表达式的方式,实现多线程程序!");
        }).start();
    }
}

3.Lambda表达式的语法

基本使用格式:

( ) -> { }
// 一些参数,一个箭头,一些代码
  • ():重写接口中唯一的抽象方法的参数列表,没有参数就空着,有多个参数使用逗号隔开
  • ->:传递,可以把参数()传递给{ }方法体使用
  • { }:重写接口中唯一抽象方法的方法体

Lambda表达式作用:简化匿名内部类

Lambda表达式的使用前提:

  • 必须有接口
  • 接口中有且只能仅有一个抽象方法(函数式接口)

注意:

  • Lambda表达式是可推导可省略
  • Lambda表达式的目的就是重写接口中唯一的抽象方法
  • 接口中的抽象方法只有一个,可以推导出重写的就是接口唯一的这个抽象方法,所以代码才能使用lambda简化

4.使用Lambda表达式重写有参数有返回值的方法

import java.util.Arrays;
import java.util.Comparator;

/*
    使用Lambda表达式重写有参数有返回值的方法(重点)
    需求:
        创建一个数组,数组的类型使用Person
        创建3个Person对象,存储到数组中
        使用数组工具类Arrays中的方法sort,根据比较器产生的规则对Person对象进行按照年龄升序排序
 */
public class Demo02Lambda {
    public static void main(String[] args) {
        //创建一个数组,数组的类型使用Person
        Person[] arr = new Person[3];

        //创建3个Person对象,存储到数组中
        arr[0] = new Person("张三",18);
        arr[1] = new Person("李四",20);
        arr[2] = new Person("王五",19);
        System.out.println("排序前数组中的元素:"+Arrays.toString(arr));

        //使用数组工具类Arrays中的方法sort,根据比较器产生的规则对Person对象进行按照年龄升序排序
        Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                //升序:o1-o2  降序:o2-o1
                return o1.getAge()-o2.getAge();
            }
        });
        System.out.println("排序后数组中的元素:"+Arrays.toString(arr));

        //使用Lambda表达式简化匿名内部类 public int compare(Person o1, Person o2)
        Arrays.sort(arr,(Person o1, Person o2)->{
            return o2.getAge()-o1.getAge();
        });
        System.out.println("排序后数组中的元素:"+Arrays.toString(arr));
    }
}

5.Lambda表达式简化格式

Lambda表达式是可推导可以省略的;

可以推导出,Lambda表达式重写的就是接口中唯一的抽象方法;

也可以推导出方法的参数和方法有没有返回值,可以对参数和返回值在进行简化

Lambda表达式的格式:

(参数列表) -> {重写抽象方法的方法体}

简化:

  • (参数列表):参数列表的数据类型是可以推导出来的,可以省略

    (int a) ==> (a)
    (int a,String s) ==> (a,s)
  • (参数列表):参数列表中只有一个参数,()小括号也可以省略,但是参数列表没有参数,()小括号不能省略

    (int a) ==> (a) ==> a
  • {重写抽象方法的方法体}:重写的方法体,如果只有一行代码(java中行的以分号分隔开的),无论是否有返回值,中括号 { } 和一行代码的结束的分号( ; )和 return 关键字可以一起省略不写

    注意:这三部分( { } ; return )或者这两部分( { } ; )必须一起省略

import java.util.Arrays;
import java.util.Comparator;

public class Demo03Lambda {
    public static void main(String[] args) {
        //使用匿名内部类的方式创建多线程程序
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"使用匿名内部类的方式,实现多线程程序!");
            }
        }).start();

        //使用Lambda表达式简化匿名内部类
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"使用Lambda表达式的方式,实现多线程程序!");
        }).start();

        //简化Lambda表达式
        new Thread(() -> System.out.println(Thread.currentThread().getName()+"使用简化Lambda表达式的方式,实现多线程程序!")).start();

        System.out.println("-------------------------");

        //创建一个数组,数组的类型使用Person
        Person[] arr = new Person[3];

        //创建3个Person对象,存储到数组中
        arr[0] = new Person("张三",18);
        arr[1] = new Person("李四",20);
        arr[2] = new Person("王五",19);
        System.out.println("排序前数组中的元素:"+ Arrays.toString(arr));

        //使用数组工具类Arrays中的方法sort,根据比较器产生的规则对Person对象进行按照年龄升序排序
        Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                //升序:o1-o2  降序:o2-o1
                return o1.getAge()-o2.getAge();
            }
        });
        System.out.println("排序后数组中的元素:"+Arrays.toString(arr));

        //使用lambda表达式简化匿名内部类
        Arrays.sort(arr,(Person o1, Person o2) -> {
            return o1.getAge()-o2.getAge();
        });
        System.out.println("排序后数组中的元素:"+Arrays.toString(arr));

        //简化Lambda表达式
        Arrays.sort(arr,(o1, o2) -> o1.getAge()-o2.getAge());
    }
}

方法引用

方法引入是JDK1.8出现的新特性,用于简化Lambda表达式。当一些内容已经存在了,就可以使用方法引用,直接引用这些已经存在的内容

1).对象名--引用成员方法

当对象和成员方法都是已经存在的时候,就可以使用对象名来直接引用成员方法

格式:

对象名::成员方法名            // :: 方法引用的运算符
import java.util.function.Supplier;

/*
    java.util.function.Supplier<T>:函数式接口
    接口中唯一的抽象方法:
        T get() :用来获取一个接口指定泛型类型的数据
    Supplier接口使用什么泛型,就可以使用get方法获取什么类型的数据
 */
public class Demo01 {
    public static void main(String[] args) {
        //调用method方法,方法的参数Supplier是一个函数式接口,所以我们可以使用Lambda表达式作为接口的实现类对象
        method(()->{
            //返回一个大写的字符串
            String s = new String("abc");
            return s.toUpperCase();
        });

        //简化Lambda表达式
        method(()->new String("abc").toUpperCase());

        /*
            字符串对象"abc"是已经存在的
            字符串中的方法toUpperCase也是已经存在的
            使用字符串对象"abc"直接引用成员方法toUpperCase
                对象名::引用成员方法
         */
        method(new String("abc")::toUpperCase);
        
        method("abc"::toUpperCase);
        method("abc"::toLowerCase);
        
        //method("abc"::show);        //Cannot resolve method 'show' show方法在String类中不存在
    }

    /*
        定义一个方法,方法的参数
            使用Supplier接口,泛型使用String
        方法内部使用Supplier接口中的get方法获取一个字符串
     */
    public static void method(Supplier<String> sup){
        String s = sup.get();
        System.out.println(s);
    }
}

2).类名--引用静态方法

如果类和静态成员方法是已经存在的,那么就可以使用类名直接引用静态成员方法

格式:

类名::引用静态成员方法
import java.util.function.Supplier;

public class Demo03 {
    public static void main(String[] args) {
        //调用method方法,方法的参数Supplier是一个函数式接口,所以我们可以使用Lambda表达式作为接口的实现类对象
        method(()->{
            //返回一个随机小数
            return Math.random();    //[0.0-1.0)
        });

        //简化Lambda表达式
        method(()-> Math.random());

        /*
            Math数学工具类是已经存在
            random静态方法已经已经存在的
            就可以直接使用Math类引用静态成员方法random,简化lambda
            Math::random:也相当于Supplier接口的实现类对象
         */
        method(Math::random);
    }

    /*
        定义一个方法,方法的参数
            使用Supplier接口,泛型使用Double
        方法内部使用Supplier接口中的get方法获取一个小数
        Supplier<Double> sup = Math::random;
     */
    public static void method(Supplier<Double> sup){
        Double d = sup.get();
        System.out.println(d);
    }
}

3).类--构造引用

如果类中的构造方法是已经存在的,使用构造方法直接引用new创建对象

格式:

构造方法::new           // Person::new ==> 使用构造方法引用new创建对象
import java.util.function.Function;

/*
    java.util.function.Function<T,R>:函数接口
    接口中唯一的抽象方法:
        R apply(T t) 根据参数类型T获取类型R类型的返回值,用于类型转换 T转换R
    例如:
        Function<String,Integer>:根据传递String类型的数据,获取Integer类型的结果  "123"==>123 = Integer.parseInt("123")
        Function<String,Person>:根据传递String类型的数据,获取Person类型的结果   "小明"==>Person p = new Person("小明")
        ...
 */
public class Demo04 {
    public static void main(String[] args) {
        //调用method方法,方法的参数Function是一个函数式接口,所以我们可以使用Lambda表达式作为接口的实现类对象
        method((String s)->{
            return new Person(s);
        },"小明");

        //简化Lambda表达式
        method(s -> new Person(s),"小红");

        /*
            Lambda表达式的目的:根据字符串的姓名,创建Person对象返回
            创建Person对象的构造方法是已经存在的public Person(String name)
            创建对象使用的关键字new已经存在的
            所以就可以使用构造方法引用new关键字直接创建对象
                构造方法::new
         */
        method(Person::new,"邓丽君");
    }

    /*
        定义一个方法,方法的参数
            使用Function接口,泛型使用<String,Person>
            传递一个字符串
        在方法内部使用Function接口中的方法apply,把字符串转换为Person对象返回
     */
    public static void method(Function<String,Person> fun,String s){
        Person p = fun.apply(s);
        System.out.println(p);
    }
}

4).数组--构造引用

格式:

数组的数据类型[]::new                    // int[]::new  创建一个int类型的数组  int[] arr = new int[10];
import java.util.Arrays;
import java.util.function.Function;

public class Demo05 {
    public static void main(String[] args) {
        //调用method方法,方法的参数Function是一个函数式接口,所以我们可以使用Lambda表达式作为接口的实现类对象
        method((Integer len)->{
            //根据数组的长度,创建一个指定长度的数组返回
            return new int[len];
        },10);

        //简化Lambda表达式
        method(len->new int[len],10);

        /*
            Lambda表达式目的:创建一个指定长度的数组  new int[len]
            数组的数据类型已经存在的int
            数组的长度已经存在的len
            使用方法引用简化Lambda表达式
            使用int[]引用关键字new,根据数组的长度创建指定长度的数组返回
                int[]::new
         */
        method(int[]::new,10);
    }

    /*
        定义一个方法,方法的参数
            使用Function接口,泛型使用<Integer,int[]>
            传递int类型数组的长度
        在方法内部,使用Function接口中的方法apply,根据传递的数组长度,创建一个指定长度的数组返回
     */
    public static void method(Function<Integer,int[]> fun,int length){
        int[] arr = fun.apply(length);
        System.out.println(Arrays.toString(arr));
    }
}

5).方法引用在Stream流中的使用(常用)

import java.util.stream.Stream;

public class Demo06Stream {
    public static void main(String[] args) {
        //获取一个Stream流
        Stream<String> stream = Stream.of("a", "b", "c");
        //使用forEach方法遍历Stream流
        //stream.forEach(s-> System.out.println(s));
        //使用方法引用简化Lambda表达式
        stream.forEach(System.out::println);
    }
}

函数式接口

1.函数式接口的定义

函数式接口:接口中有且仅有一个抽象方法的接口

函数式接口一般都作为方法的参数(另外也可以作为返回值类型)

  • 方法的参数是一个函数式接口,那么就可以传递lambda表达式作为接口的一种实现类对象为方法的参数接口变量进行赋值
  • 方法的返回值类型是一个函数式接口,方法内部就可以返回一个Lambda表达式作为接口的实现类对象

注意:

  • 接口中没有抽象方法不行
  • 接口中除了唯一的抽象方法,还可以包含其他的方法(默认,静态,私有)
@FunctionalInterface        // 注解@FunctionalInterface:检测接口是否为一个函数式接口
public interface MyFunction {
    //定义抽象方法
    public abstract void show(int a);

    public static void show02(){
        System.out.println("静态方法!");
    }

    public default void show03(){
        System.out.println("默认方法!");
    }
}

2.函数式接口的使用

函数式接口的使用:和使用普通的接口是一样的用法

  • 在工作中,一般都使用函数式接口作为方法的参数类型或者返回值类型使用
  • 调用方法传递的参数和方法返回的值,就可以使用Lambda表达式来简化了
  • Lambda表达式可以看成就是接口实现类对象的一种
public class Demo01MyFunction {
    public static void main(String[] args) {
        //调用method方法,方法的参数MyFunction是一个接口,可以传递接口的实现类对象,给接口变量赋值
        method(new MyFunction() {
            @Override
            public void show(int a) {
                System.out.println("匿名内部类的方式,重写show方法-->"+a);
            }
        },10);

        //调用method方法,方法的参数MyFunction是一个函数式接口,可以传递接口的实现类对象,给接口变量赋值
        //使用Lambda表达式作为接口的实现类对象
        method((int a)->{
            System.out.println("Lambda表达式的方式,重写show方法-->"+a);
        },10);

        //简化lambda表达式
        method(a->System.out.println("Lambda表达式的方式,重写show方法-->"+a),10);

        System.out.println("------------------------------");
        //多态 接口 = 实现类对象
        //MyFunction my = a->System.out.println("方法返回的是一个Lamda表达式:"+a);
        MyFunction my = getInstance();
        my.show(100);
    }

    /*
        定义一个方法,方法的参数类型使用函数式接口MyFunction
        //多态 接口     匿名内部类对象
        MyFunction my = new MyFunction() {
            @Override
            public void show(int a) {
                System.out.println("匿名内部类的方式,重写show方法-->"+a);
            }
        };
        ------------------
        MyFunction my = (int a)->{
            System.out.println("Lambda表达式的方式,重写show方法-->"+a);
        }
     */
    public static void method(MyFunction my,int i){
        my.show(i);
    }

    /*
        定义一个方法,方法的返回值类型使用函数式接口MyFunction
     */
    public static MyFunction getInstance(){
        //方法的返回值类型是MyFunction接口,方法就需要返回一个MyFunction接口的实现类对象
        /*return new MyFunction() {
            @Override
            public void show(int a) {
                System.out.println("方法返回的是一个匿名内部类对象:"+a);
            }
        };*/

        //方法的返回值类型是MyFunction函数式接口,方法就需要返回一个Lambda表达式作为MyFunction接口的实现类对象
        /*return (int a)->{
            System.out.println("方法返回的是一个Lamda表达式:"+a);
        };*/

        //简化lambda表达式
        return a->System.out.println("方法返回的是一个Lamda表达式:"+a);
    }
}

3.函数式接口:Consumer

java.util.function.Consumer:函数式接口-消费者

接口中唯一的抽象方法:

 void accept(T t)    //消费一个指定泛型类型的数据

Consumer接口的泛型使用什么类型,就可以使用accept方法消费(使用)一个什么类型的数据

至于我们怎么使用这个数据,看心情(想怎么使用就怎么使用,可以计算,可以输出...)

import java.util.function.Consumer;

public class Demo02Consumer {
    public static void main(String[] args) {
        //调用method方法,方法的参数Consumer是一个接口,可以传递接口的匿名内部类对象,给接口变量赋值
        method(new Consumer<String>() {
            @Override
            public void accept(String s) {
                //怎么使用这个参数,看心情
                System.out.println(s);
            }
        },"aaa");

        //调用method方法,方法的参数Consumer是一个函数式接口,可以传递lambda表达式作为接口的实现类对象,给接口变量赋值
        method((String s)->{
            System.out.println(s);
        },"abc");

        //简化Lambda表达式
        method(s-> System.out.println(s),"cba");
    }

    /*
        定义一个方法,方法的参数
            使用Consumer接口,泛型使用String
            使用一个字符串参数
        在方法的内部使用Consumer接口中的方法accept,对字符串参数进行消费
     */
    public static void method(Consumer<String> con,String s){
        con.accept(s);
    }
}

4.函数式接口:Supplier

java.util.function.Supplier:函数式接口-供应商

接口中唯一的抽象方法:

T get()        // 用来获取接口指定泛型类型的数据

Supplier接口使用什么泛型,就可以使用get方法获取一个什么类型的数据

import java.util.Random;
import java.util.function.Supplier;

public class Demo05Supplier {
    public static void main(String[] args) {
        //调用method方法,方法的参数Supplier是一个接口,可以传递接口的匿名内部类对象,给接口变量赋值
        method(new Supplier<Integer>() {
            @Override
            public Integer get() {
                return 10;
            }
        });

        //调用method方法,方法的参数Supplier是一个函数式接口,可以传递lambda表达式作为接口的实现类对象,给接口变量赋值
        method(() -> {
            return 10;
        });

        //简化Lambda表达式
        method(() -> 10);
        method(() -> new Random().nextInt(100));
    }

    /*
        定义一个方法,方法的参数
            使用Supplier接口,接口的泛型使用Integer
        在方法内部使用Supplier接口中的get方法,获取一个Integer类型的数据返回
     */
    public static void method(Supplier<Integer> sup){
        Integer in = sup.get();
        System.out.println(in);
    }
}

5.函数式接口:Predicate

java.util.function.Predicate:函数式接口-类型判断

接口中唯一的抽象方法:

boolean test(T t)    //用于对接口指定泛型类型的数据进行判断

Predicate接口的泛型使用什么类型,就可以使用test方法判断数据是否满足要求

  • 满足要求:返回true
  • 不满足安全:返回false
import java.util.function.Predicate;

public class Demo03Predicate {
    public static void main(String[] args) {
        //调用method方法,方法的参数Predicate是一个接口,可以传递接口的匿名内部类对象,给接口变量赋值
        method(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                //判断的规则:判断字符串的长度是否大于5
                return s.length()>5;
            }
        },"aaa");

        //调用method方法,方法的参数Predicate是一个函数式接口,可以传递lambda表达式作为接口的实现类对象,给接口变量赋值
        method((String s) -> {
            return s.length()>5;
        },"aaaaaa");

        //简化Lambda表达式
        method(s -> s.length()>5,"1111");
    }

    /*
        定义一个方法,方法的参数
            使用Predicate接口,泛型使用String
            使用String类型的参数
        在方法内部使用Predicate接口中的方法test对字符串进行判断,是否满足要求
     */
    public static void method(Predicate<String> pre,String s){
        boolean b = pre.test(s);
        System.out.println(b);
    }
}

6.函数式接口:Function

java.util.function.Function<T,R>:函数接口-类型转换

接口中唯一的抽象方法:

R apply(T t)    //根据参数类型T获取类型R类型的返回值,用于类型转换 T转换R

例如:

// 根据传递String类型的数据,获取Integer类型的结果  "123" ==> 123 = Integer.parseInt("123")
Function<String, Integer>

// 根据传递String类型的数据,获取Person类型的结果   "小明" ==> Person p = new Person("小明")
Function<String, Person>
import java.util.function.Function;

public class Demo04Function {
    public static void main(String[] args) {
        //调用method方法,方法的参数Function是一个接口,可以传递接口的匿名内部类对象,给接口变量赋值
        method(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                //把字符串整数,转换为Integer类型返回
                return Integer.parseInt(s);
            }
        },"10");

        //调用method方法,方法的参数Function是一个函数式接口,可以传递lambda表达式作为接口的实现类对象,给接口变量赋值
        method((String s)->{
            return Integer.parseInt(s);
        },"20");
        
        //简化Lambda表达式
        method(s->Integer.parseInt(s),"30");
    }

    /*
        定义一个方法,方法的参数
            使用Function接口,接口的泛型使用<String,Integer>
            使用一个String类型的参数
        在方法内部使用Function接口中的方法apply,把String类型的数据转换为Integer类型返回
     */
    public static void method(Function<String,Integer> fun,String s){
        Integer in = fun.apply(s);
        System.out.println(in+100);
    }
}
相关文章
|
6月前
Lambda表达式方法引用举例
Lambda表达式方法引用举例
28 1
|
2月前
|
C#
C#一分钟浅谈:Lambda 表达式和匿名方法
本文详细介绍了C#编程中的Lambda表达式与匿名方法,两者均可用于定义无名函数,使代码更简洁易维护。文章通过基础概念讲解和示例对比,展示了各自语法特点,如Lambda表达式的`(parameters) =&gt; expression`形式及匿名方法的`delegate(parameters)`结构。并通过实例演示了两者的应用差异,强调了在使用Lambda时应注意闭包问题及其解决策略,推荐优先使用Lambda表达式以增强代码可读性。
42 8
Lambda 语法糖《方法引用》
Lambda 语法糖《方法引用》
|
6月前
|
JavaScript 前端开发 Java
23、匿名类的排序、Lambda 表达式、方法引用
23、匿名类的排序、Lambda 表达式、方法引用
41 0
|
6月前
|
存储 算法 Java
函数式接口和lambda表达式优雅的替换大量的if-else
函数式接口和lambda表达式优雅的替换大量的if-else
217 0
函数包装器和lambda表达式
函数包装器和lambda表达式
|
Java
3.2 函数式接口与Lambda表达式的实际应用:函数式接口作为方法参数和返回值
3.2 函数式接口与Lambda表达式的实际应用:函数式接口作为方法参数和返回值
63 0
|
存储 Java 开发者
1.1 Lambda表达式的基础:Lambda表达式的定义与语法
1.1 Lambda表达式的基础:Lambda表达式的定义与语法
119 0
|
Java 开发者
1.4 Lambda表达式的基础:Lambda表达式与匿名类的对比
1.4 Lambda表达式的基础:Lambda表达式与匿名类的对比
92 0
函数式接口概述、作为方法的参数、作为方法的返回值及函数式接口Supplier介绍
函数式接口概述、作为方法的参数、作为方法的返回值及函数式接口Supplier介绍
66 0