Lambda表达式与方法引用

1 Lambda表达式

1.1 函数式编程思想概述

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿数据做数据”

面向对象思想强调“必须通过对象的形式来做事情”

函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”

而我们要学的Lambda表达式就是函数式思想的体现

1.2 体验Lambda表达式

需求:启动一个线程,在控制台输出一句话:多线程程序启动了

方式1:

  • 定义一个类MyRunnable实现Runnable接口,重写run()方法
  • 创建MyRunnable类的对象
  • 创建Thread类的对象,把MyRunnable的对象作为构造参数传递
  • 启动线程
// MyRunnable类实现了Runnable接口
public class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println("多线程程序启动了");
    }
}
// Test类
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread,"Yuki");
        thread.start();
    }
}

方式2

  • 匿名内部类的方式改进
// 以上代码为了实现一个输出语句,过于麻烦,所以要进行简化
public class Test {
    public static void main(String[] args) {
        // 用匿名内部类的方式改进
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多线程程序启动了");
            }
        },"Yuki").start();
    }
}

方式3

  • Lambda表达式的方式改进
// 以上代码如果忘了格式,是写不出来的,以下为Lambda表达式的写法
public class Test {
    public static void main(String[] args) {
        // Lambda表达式的写法
        // Lambda表达式的写法
        new Thread( () ->{
            System.out.println("多线程程序启动了");
        } ).start();
    }
}

1.3 Lambda表达式的标准格式

匿名内部类中重写run()方法的代码分析

new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多线程程序启动了");
            }
        },"Yuki").start();
  • 方法形式参数为空,说明调用方法时不需要传递参数
  • 方法返回值类型为void,说明方法执行没有返回结果
  • 方法体重的内容,是我们具体要做的事

Lambda表达式的代码分析

new Thread( () ->{
            System.out.println("多线程程序启动了");
        } ).start();
  • ():里没有内容,可以看成是方法形式参数为空
  • ->:用箭头指向后面要做的事情
  • {}:包含一段代码,我们称之为代码块,可以看成是方法体中的内容

组成Lambda表达式的三要素:形式参数箭头代码块

Lambda表达式的格式

  • 格式:(形式参数)->(代码块)
  • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
  • ->:由英文中画线和大于符号组成,固定写法。代表指向动作
  • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容

Lambda表达式的使用前提

  • 有一个接口
  • 接口中有且仅有一个方法
  • 有一个方法去调用这个接口

1.4 Lambda表达式的使用

练习1:

  • 定义一个接口(Eatable),里面定义一个抽象方法:void eat();
  • 定义一个测试类(EatableDemo),在测试类中提供两个方法
    • 一个方法是:useEatable(Eatable e)
    • 一个方法是主方法,在主方法中调用useEatable方法
// Eatable接口
public interface Eatable {
    void eat();
}

// 用EatableImpl实现该接口
public class EatableImpl implements Eatable {
    @Override
    public void eat() {
        System.out.println("一天一苹果,医生远离我");
    }
}

// 主方法
public class EatableDemo {
    public static void main(String[] args) {
        // 在主方法中调用useEatable方法
        Eatable e = new EatableImpl();
        useEatable(e);
        // 匿名内部类
        useEatable(new Eatable() {
            @Override
            public void eat() {
                System.out.println("我快被Lambda表达式整疯了");
            }
        });
        // Lambda表达式
        useEatable(() -> {
            System.out.println("\"龙门粗口\",竟然还能这么用");
        });
    }

    public static void useEatable(Eatable e) {
        e.eat();
    }
}

练习2:

  • 定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s);
  • 定义一个测试类(FlyableDemo),在测试类中提供两个方法
    • 一个方法是:useFlyable(Flyable f)
    • 一个方法是主方法,在主方法中调用useFlyable方法
// 接口
public interface Flyable {
    void fly(String s);
}

// 主方法
public class FlyableDemo {
    public static void main(String[] args) {

        useFlyable(new Flyable() {
            @Override
            public void fly(String s) {
                System.out.println(s);
            }
        });
        useFlyable((String s)->{
            System.out.println(s);
        });
    }

    public static void useFlyable(Flyable f) {
        // 匿名内部类和Lambda表达式中的形参是在这里传输的
        f.fly("我要飞上天");
    }
}

练习3:

  • 定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y);
  • 定义一个测试类(AddableDemo),在测试类中提供两个方法
    • 一个方法是:useAddable(Addable a)
    • 一个方法是主方法,在主方法中调用useAddable方法
// 接口
public interface Addable {
    int add(int x, int y);
}
// 主方法
public class AddableDemo {
    public static void main(String[] args) {
        useAddable((int x,int y)->{
            return x+y;
        });
    }
    public static void useAddable(Addable a) {
        int sum = a.add(10, 20);
        System.out.println(sum);
    }
}

1.5 Lambda表达式的省略模式

省略规则:

  • 参数类型可以省略,但是有多个参数的情况下,不能只省略一个
  • 如果参数有且仅有一个,小括号可以省略
  • 如果代码块的语句只有一条,可以省略大括号,如果只有一条return语句,要省略return
// 接口1
public interface Flyable {
    void fly(String s);
}
// 接口2
public interface Addable {
    int add(int x, int y);
}
// 主方法
public class LambdaDemo {
    public static void main(String[] args) {
        // 参数类型可以省略,但是有多个参数的情况下,不能只省略一个
        // 这里如果写(x,int y)会报错
        useAddable((x, y) -> {
            return x + y;
        });
        // 如果参数有且仅有一个,小括号可以省略
        useFlyable(s -> {
            System.out.println(s);
        });
        // 如果代码块的语句只有一条,可以省略大括号,如果只有一条return语句,要省略return
        useFlyable(s -> System.out.println(s));
        // 如果代码块的语句只有一条,可以省略大括号,如果只有一条return语句,要省略return
        // 这里写(x, y) -> return x + y会报错
        useAddable((x, y) -> x + y);
    }

    public static void useAddable(Addable a) {
        int sum = a.add(10, 20);
        System.out.println(sum);
    }

    public static void useFlyable(Flyable f) {
        f.fly("自从用了Lambda表达式,每天都在想着怎么偷懒写代码");
    }
}

1.6 Lambda表达式的注意事项

注意事项:

  • 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
  • 必须要有上下文环境,才能推导出Lambda对应的接口
    • 根据局部变量的赋值得知Lambda对应的接口:

      • Runnable r = () -> System.out.println("Lambda表达式");
    • 根据调用方法的参数得知Lambda对应的接口:
      • new Thread(() -> System.out.println("Lambda表达式")).start();

1.7 Lambda表达式和匿名内部类的区别

所需类型不同

  • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
  • Lambda表达式:只能是接口

使用限制不同

  • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
  • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

实现原理不同

  • 匿名内部类:编译之后,产生一个单独的.class字节码文件
  • Lambda表达式:编译之后,没有一个单独的.class文件.对应的字节码会在运行的时候动态生成
// 接口类
public interface Inter {
    void show();
    // void show2();
}

// 具体类
public class Student {
    public void study(){
        System.out.println("爱学习,爱Java");
    }
}

// 抽象类
public abstract class Animal {
    public abstract void method();
}

// 主方法
/*
    Lambda表达式和匿名内部类的区别
 */
public class LambdaDemo {
    public static void main(String[] args) {
        // 匿名内部类
        useStudent(new Student(){
            @Override
            public void study() {
                System.out.println("具体类");
            }
        });
        useAnimal(new Animal() {
            @Override
            public void method() {
                System.out.println("抽象类");
            }
        });
        useInter(new Inter() {
            @Override
            public void show() {
                System.out.println("接口");
            }
        });
        // Lambda
        useInter(() -> System.out.println("接口"));
        // 报错
        // useAnimal(() -> System.out.println("抽象类"));
        // 报错
        // useStudent(()-> System.out.println("具体类"));
        /*
            结论:使用Lambda时,形参必须是一个接口,不能是抽象类或者具体类
         */

        // 接口中有两个抽象方法,那么不能使用Lambda
        // useInter(() -> System.out.println("接口"));

        // 可以使用匿名内部类
        useInter(new Inter() {
            @Override
            public void show() {
                System.out.println("接口");
            }

            @Override
            public void show2() {
                System.out.println("接口");
            }
        });

        /*
            匿名内部类会生成一个额外的.class的字节码文件,而Lambda表达式不会,对应的字节码会在运行过程中动态生成.
         */
    }

    public static void useStudent(Student s) {
        s.study();
    }

    public static void useAnimal(Animal a) {
        a.method();
    }

    public static void useInter(Inter i) {
        i.show();
    }
}

2 接口组成更新

2.1 接口组成更新概述

接口的组成

  • 常量

    • public static final
  • 抽象方法
    • public abstract
  • 默认方法(java8)
  • 静态方法(java8)
  • 私有方法(java9)

2.2 接口中的默认方法

需求:

  1. 定义一个接口MyInterface,里面有两个抽象方法:

    • void show1();
    • void show2();
  2. 定义接口的两个实现类
    • MyInterfaceImplOne
    • MyInterfaceImplTwo
  3. 定义测试类:
    • MyInterfaceDemo
    • 在主方法中,按照多态的方式创建对象并使用

接口中默认方法的定义格式:

  • 格式:public default 返回值类型 方法名(参数列表){}
  • 范例:public default void show3(){}

接口中默认方法的注意事项

  • 默认方法不是抽象方法,所以不强制被重写.但是可以被重写,重写的时候去掉default关键字
  • public可以省略,default不能省略
/*
需求:
    1. 定义一个接口MyInterface,里面有两个抽象方法:
       - void show1();
       - void show2();
    2. 定义接口的两个实现类
       - MyInterfaceImplOne
       - MyInterfaceImplTwo
    3. 定义测试类:
       - MyInterfaceDemo
       - 在主方法中,按照多态的方式创建对象并使用
 */

// 主方法
public class MyInterfaceDemo {
    public static void main(String[] args) {
        // 按照多态的方式创建对象并使用
        MyInterface one = new MyInterfaceImplOne();
        MyInterface two = new MyInterfaceImplTwo();
        one.show1();
        two.show2();
        one.show3();
    }
}

// 接口类
public interface MyInterface {
    void show1();
    void show2();
    // 这种写法不会影响已经实现了此接口的子接口,想重写需要在实现接口的子接口中去掉default关键字
    default void show3(){
        System.out.println("show3");
    }
}

// 子接口类1
public class MyInterfaceImplOne implements MyInterface {
    @Override
    public void show1() {
        System.out.println("One show1");

    }

    @Override
    public void show2() {
        System.out.println("One show2");
    }

    // 重写show3时去掉了default
    @Override
    public void show3() {
        System.out.println("One show3");
    }
}

// 子接口类2
// 没有实现show3,不影响
public class MyInterfaceImplTwo implements MyInterface {
    @Override
    public void show1() {
        System.out.println("Two show1");
    }

    @Override
    public void show2() {
        System.out.println("Two show2");
    }
}

2.3 接口中的静态方法

接口中静态方法的定义格式:

  • 格式:public static 返回值类型 方法名(参数列表){}
  • 范例:public static void show(){}

需求:

  1. 定义一个接口Inter,里面有三个方法:一个是抽象方法,一个是默认方法,一个是静态方法

    • void show();
    • default void method(){}
    • public static void test(){}
  2. 定义接口的一个实现类
    • InterImpl
  3. 定义测试类
    • InterDemo
    • 在主方法中,按照多态的方式创建对象并使用

接口中的静态方法注意事项:

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
  • public可以省略,static不能省略
/*
    需求:
        1. 定义一个接口Inter,里面有三个方法:一个是抽象方法,一个是默认方法,一个是静态方法
           - void show();
           - default void method(){}
           - public static void test(){}
        2. 定义接口的一个实现类
           - InterImpl
        3. 定义测试类
           - InterDemo
           - 在主方法中,按照多态的方式创建对象并使用
 */
// 主方法
public class InterDemo {
    public static void main(String[] args) {
        Inter i = new InterImpl();
        i.show();
        i.method();
        // 报错,该方法只能被接口调用
        // i.test();
        // InterImpl.test();
        // 发现运行成功
        Inter.test();
    }
}
// 实现接口的子类
public class InterImpl implements Inter {
    @Override
    public void show() {
        System.out.println("show方法被执行了");
    }
}
// 接口
public interface Inter {
    void show();

    default void method() {
        System.out.println("Inter中的默认方法被执行了");
    }

    static void test() {
        System.out.println("Inter中的静态方法被执行了");
    }
}

2.4接口中的私有方法

Java 9中新增了带方法体的私有方法,这其实在Java8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法.这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是java9增加私有方法的必然性

接口中私有方法的定义格式:

  • 格式1:private 返回值类型 方法名(参数列表){}
  • 范例1:private void show(){}
  • 格式2:private static 返回值类型 方法名(参数列表){}
  • 范例2:private static void method(){}

需求:

  1. 定义一个接口Inter,里面有四个方法:2个默认方法,2个静态方法

    • default void show1(){}
    • default void show2(){}
    • static void method1(){}
    • static void method2(){}
  2. 定义接口的一个实现类
    • InterImpl
  3. 定义测试类
    • InterDemo
    • 在主方法中,按照多态的方式创建对象并使用

接口中私有方法的注意事项:

  • 默认方法可以只调用私有的静态方法和非静态方法
  • 静态方法只能调用私有的静态方法
/*
    需求:
        1. 定义一个接口Inter,里面有四个方法:2个默认方法,2个静态方法
           - default void show1(){}
           - default void show2(){}
           - static void method1(){}
           - static void method2(){}
        2. 定义接口的一个实现类
           - InterImpl
        3. 定义测试类
           - InterDemo
           - 在主方法中,按照多态的方式创建对象并使用
 */
// 主方法
public class InterDemo {
    public static void main(String[] args) {
        Inter i = new InterImpl();
        i.show1();
        i.show2();
        Inter.method1();
        Inter.method2();
    }
}
// 实现接口的子接口
public class InterImpl implements Inter{

}
// 接口
public interface Inter {
    private void show() {
        System.out.println("初级工程师");
        System.out.println("中级工程师");
        System.out.println("高级工程师");
    }

    private static void method() {
        System.out.println("初级工程师");
        System.out.println("中级工程师");
        System.out.println("高级工程师");
    }

    default void show1() {
        System.out.println("show1开始执行");
//        System.out.println("初级工程师");
//        System.out.println("中级工程师");
//        System.out.println("高级工程师");
        show();
        System.out.println("show1结束执行");
    }

    default void show2() {
        System.out.println("show2开始执行");
//        System.out.println("初级工程师");
//        System.out.println("中级工程师");
//        System.out.println("高级工程师");
        method();
        System.out.println("show2结束执行");
    }

    static void method1() {
        System.out.println("method1开始执行");
//        System.out.println("初级工程师");
//        System.out.println("中级工程师");
//        System.out.println("高级工程师");
        method();
        System.out.println("method1结束执行");
    }

    static void method2() {
        System.out.println("method2开始执行");
//        System.out.println("初级工程师");
//        System.out.println("中级工程师");
//        System.out.println("高级工程师");
        method();
        System.out.println("method2结束执行");
    }
}

3 方法引用

3.1 体验方法引用

在使用Lambda表达式的时候,我们实际上传递进去的代码就像是一种解决方案:拿参数做操作

  • 那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那么是否还有必要再写重复逻辑呢?

    答案肯定是没有必要

  • 那我们又是如何使用已经存在的方案的呢?

    这就是我们要讲解的方法引用,我们通过方法引用来使用已经存在的方案

需求:

  1. 定义一个接口(Printable):里面定义一个抽象方法:void printString(String s);
  2. 定义一个测试类(PrintableDemo):在测试类中提供两个方法
    • 一个方法是:usePrintable(Printable p)
    • 一个方法是主方法,在主方法中调用usePrintable方法
/*
    需求:
        1. 定义一个接口(Printable):里面定义一个抽象方法:void printString(String s);
        2. 定义一个测试类(PrintableDemo):在测试类中提供两个方法
           - 一个方法是:usePrintable(Printable p)
           - 一个方法是主方法,在主方法中调用usePrintable方法
 */
public class PrintableDemo {
    public static void main(String[] args) {
        usePrintable((s) -> System.out.println(s));
        /*
            实际上输出这个字符串我们在初学java时就已经学过了,即
            System.out.println("键盘敲烂,月薪过万");
            也就是说用Lambda要解决的事情是有解决方案存在的
            用的是System.out这个对象的println方法
            下面做一个代码改建
         */
        // 方法引用符:   ::
        // println有很多重载形式,而传入的参数会根据自身类型推导
        // 可推导的即可省略的
        usePrintable(System.out::println);
    }

    public static void usePrintable(Printable p) {
        p.printString("键盘敲烂,月薪过万");
    }
}

// 接口
public interface Printable {
    void printString(String s);
}

3.2 方法引用符

方法引用符

  • ::该符号为引用运算符,而它所在的表达式被称为方法引用

回顾一下我们在体验方法引用中的代码

  • Lambda表达式:usePrintable(s -> System.out.println)方法去处理

    • 分析:拿到参数s之后通过Lambda表达式,传递给System.out.println方法去处理
  • 方法引用:usePrintable(System.out::println)
    • 分析:直接使用System.out中的println方法来取代Lambda,代码更加简洁

推导与省略

  • 如果使用Lambda,那么根据"可推导就是可省略"的原则,无需指定参数类型,也无需指定重载形式,他们都将被自动推导
  • 如果使用方法引用,也是同样可以根据上下文进行推导
  • 方法引用是Lambda的孪生兄弟

需求:

  1. 定义一个接口(Printable):里面定义一个抽象方法:void printInt(int i)
  2. 定义一个测试类(PrintableDemo),在测试类中提供两个方法
    • 一个方法是:usePrintable(Printable p)
    • 一个方法是主方法,在主方法中调用usePrintable方法
/*
需求:
    1. 定义一个接口(Printable):里面定义一个抽象方法:void printInt(int i)
    2. 定义一个测试类(PrintableDemo),在测试类中提供两个方法
       - 一个方法是:usePrintable(Printable p)
       - 一个方法是主方法,在主方法中调用usePrintable方法
 */
public class PrintableDemo {
    public static void main(String[] args) {
        // Lambda表达式
        usePrintable(i-> System.out.println(i));
        // 方法引用
        usePrintable(System.out::println);
    }

    private static void usePrintable(Printable p) {
        p.printInt(1);
    }
}

// 接口
public interface Printable {
    void printInt(int i);
}

3.3 Lambda表达式支持的方法引用

常见的引用方式:

  • 引用类方法
  • 引用对象的实例方法
  • 引用类的实例方法
  • 引用构造器

3.3.1 引用类方法

引用类方法,其实就是引用类的静态方法

  • 格式:类名::静态方法
  • 范例:Integer::parseInt
    • Integer类的方法:public static int parseInt(String s)将此String转换为int类型数据

练习:

  • 定义一个接口(Converter),里面定义一个抽象方法

    • int convert(String s);
  • 定义一个测试类(ConverterDemo),在测试类中提供两个方法
    • 一个方法是:useConverter(Converter c)
    • 一个方法是主方法,在主方法中调用useConverter方法
/*
    练习:
        - 定义一个接口(Converter),里面定义一个抽象方法
          - int convert(String s);
        - 定义一个测试类(ConverterDemo),在测试类中提供两个方法
          - 一个方法是:useConverter(Converter c)
          - 一个方法是主方法,在主方法中调用useConverter方法
 */
public class ConverterDemo {
    public static void main(String[] args) {
        // Lambda表达式
        useConverter(s -> Integer.parseInt(s));
        // 方法引用
        useConverter(Integer::parseInt);
        // Lambda表达式被类方法代替的时候,它的形参全部传递给静态方法作为参数
    }

    public static void useConverter(Converter c) {
        int i = c.convert("123");
        System.out.println(i);
    }
}
public interface Converter {
    int convert(String s);
}

Lambda表达式被类方法代替的时候,它的形参全部传递给静态方法作为参数

3.3.2 引用对象的实例方法

引用对象的实例方法,其实就是引用类中的成员方法

  • 格式:对象::成员方法
  • 范例:"HelloWorld"::toUpperCase
    • String类中的方法:public String toUpperCase()将此String所有字符转换为大写

练习:

  • 定义一个类(PrintString),里面定义一个方法

    • public void printUpper(String s):把字符串参数变成大写的数据,然后在控制台输出
  • 定义一个接口(Pointer),里面定义一个抽象方法
    • void printUpperCase(String s)
  • 定义一个测试类(PrinterDemo),在测试类中提供两个方法
    • 一个方法是:usePrinter(Printer p)
    • 一个方法是主方法,在主方法中调用usePrinter方法
/*
    练习:
        - 定义一个类(PrintString),里面定义一个方法
          - public void printUpper(String s):把字符串参数变成大写的数据,然后在控制台输出
        - 定义一个接口(Pointer),里面定义一个抽象方法
          - void printUpperCase(String s)
        - 定义一个测试类(PrinterDemo),在测试类中提供两个方法
          - 一个方法是:usePrinter(Printer p)
          - 一个方法是主方法,在主方法中调用usePrinter方法
 */
public class PrinterDemo {
    public static void main(String[] args) {
        // Lambda表达式
        usePrinter(s-> System.out.println(s.toUpperCase()));
        // 方法引用
        PrintString ps = new PrintString();
        usePrinter(ps::printUpper);
        // Lambda表达式被对象的实例方法替代的时候,它的形参全部传递给该方法作为参数
    }

    private static void usePrinter(Printer p) {
        p.printUpperCase("HelloWorld");
    }
}

public class PrintString {
    public void printUpper(String s){
        String s1 = s.toUpperCase();
        System.out.println(s1);
    }
}

public interface Printer {
    void printUpperCase(String s);
}

Lambda表达式被对象的实例方法替代的时候,它的形参全部传递给该方法作为参数

3.3.3 引用类的实例方法

引用类的实例方法,其实就是引用类中的成员方法

  • 格式:类名::成员方法
  • 范例:String::substring
    • String类中的方法:public String substring(int beginIndex,int endIndex)
    • 从beginIndex开始到endIndex结束,截取字符串,返回一个子串,子串的长度为endIndex-beginIndex

练习

  • 定义一个接口(MyString),里面定义一个抽象方法:

    • String mySubString(String s,int x,int y);
  • 定义一个测试类(MyStringDemo),在测试类中提供两个方法
    • 一个方法是:useMyString(MyString my);
    • 一个方法是主方法,在主方法中调用useMyString方法
/*
    练习
        - 定义一个接口(MyString),里面定义一个抽象方法:
          - String mySubString(String s,int x,int y);
        - 定义一个测试类(MyStringDemo),在测试类中提供两个方法
          - 一个方法是:useMyString(MyString my);
          - 一个方法是主方法,在主方法中调用useMyString方法
 */
public class MyStringDemo {
    public static void main(String[] args) {
        // Lambda表达式
        useMyStrint((s,x,y)-> s.substring(x,y));
        // 方法引用
        useMyStrint(String::substring);
        /*
            Lambda表达式被类的实例方法代替的时候
            第一个参数作为调用者
            后面的参数全部传递给该方法作为参数
         */
    }

    private static void useMyStrint(MyString my) {
        String result = my.mySubString("键盘敲烂,月薪过万", 0, 9);
        System.out.println(result);
    }

}
public interface MyString {
    String mySubString(String s,int x,int y);
}
Lambda表达式被类的实例方法代替的时候
第一个参数作为调用者
后面的参数全部传递给该方法作为参数

3.3.4 引用构造器

引用构造器,其实就是引用构造方法

  • 格式:类名::new
  • 范例:Student::new

练习:

  • 定义一个类(Student),里面有两个成员变量(name,age)并提供无参构造方法和带参构造方法,以及成员变量对应的get/set方法
  • 定义一个接口(StudentBuilder),里面定义一个抽象方法Student build(String name,int age);
  • 定义一个测试类(StudentDemo),在测试类重提供两个方法
    • 一个方法是:useStudentBuilder(StudentBuilder s)
    • 一个方法是主方法,在主方法中调用useStudentBuilder方法
/*
练习:
- 定义一个类(Student),里面有两个成员变量(name,age)并提供无参构造方法和带参构造方法,以及成员变量对应的get/set方法
- 定义一个接口(StudentBuilder),里面定义一个抽象方法Student build(String name,int age);
- 定义一个测试类(StudnentDemo),在测试类重提供两个方法
  - 一个方法是:useStudentBuilder(StudentBuilder s)
  - 一个方法是主方法,在主方法中调用useStudentBuilder方法
 */
public class StudentDemo {
    public static void main(String[] args) {
        // Lambda表达式
        useStudentBuilder((name,age)->new Student(name,age));
        // 方法引用
        useStudentBuilder(Student::new);
        /*
            Lambda表达式被构造器代替的时候,它的形式参数全部传递给构造器作为参数
         */
    }

    private static void useStudentBuilder(StudentBuilder s) {
        s.build("Yuki",14);
    }
}

public interface StudentBuilder {
    Student build(String name,int age);
}

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println(toString());
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Lambda表达式被构造器代替的时候,它的形式参数全部传递给构造器作为参数

原文地址:https://www.cnblogs.com/clevergirl/p/11405447.html

时间: 2024-10-09 20:25:52

Lambda表达式与方法引用的相关文章

Java8 之 lambda 表达式、方法引用、函数式接口、默认方式、静态方法

今天我来聊聊 Java8 的一些新的特性,确实 Java8 的新特性的出现,给开发者带来了非常大的便利,可能刚刚开始的时候会有点不习惯的这种写法,但是,当你真正的熟悉了之后,你一定会爱上这些新的特性的,这篇文章就来聊聊这些新特性. lambda 表达式 lambda 表达式在项目中也是用到了,这种新的语法的加入,对于使用 Java 多年的我,我觉得是如虎添翼的感觉哈,这种新的语法,大大的改善了以前的 Java 的代码,变得更加的简洁,我觉得这也是为什么 Java8 能够很快的流行起来的原因吧.

Lambda 表达式与方法引用(二)

在上一章我们介绍了函数式编程的概念和函数式接口.Lambda 表达式就是函数式编程的具体体现,它需要借助函数式接口才能应用在 Java 语言中. 定义 在编程语言中,lambda 表达式是一种用于指定匿名函数或者闭包的运算符.Lambda 可以很清晰地表达一个匿名函数,可以被传递.有了 Lambda 表达式之后,Lambda 表达式为 Java 添加了缺失的函数式编程特性,使我们能将函数当作一等公民看待. 在将函数作为一等公民的语言中,Lambda 表达式的类型是函数.但在 Java 中,Lam

java8之lambda表达式(方法引用)

有些时候,你想要传递给其他代码的操作已经有实现的方法了.示例: button.setOnAction(event -> System.out.println(event); 如果你能够只将println方法传递给setOnAction方法,就更好了!下面是改后的代码: button.setOnAction(System.out::println); 表达式System.out::println是一个方法引用,等同于lambda表达式: x -> System.out.println(x) 正如

Java 8 新特性:Lambda 表达式之方法引用(Lambda 表达式补充版)——诺诺"涂鸦"记忆

----------   诺诺学习技术交流博客.期待与您交流!    ---------- 详情请查看:http://blog.csdn.net/sun_promise  方法引用 (注:此文乃个人查找资料然后学习总结的,若有不对的地方,请大家指出,非常感谢!) 1.方法引用简述 方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法.方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文.计算时,方法引用会创建函数式接口的一个实例. 当Lambda表达式中

JDK8的lambda表达式、方法引用

(部分转自:https://www.cnblogs.com/xiaoxi/p/7099667.html) 1. lambda表达式 以前定义一个Thread: 1 final int i = 0; 2 new Thread(new Runnable() { 3 @Override 4 public void run() { 5 System.out.println("i = " + i); 6 } 7 }).start(); lambda表达式定义: 1 int i = 0; 2 ne

编写高质量代码改善C#程序的157个建议——建议37:使用Lambda表达式代替方法和匿名方法

建议37:使用Lambda表达式代替方法和匿名方法 在建议36中,我们创建了这样一个实例程序: static void Main(string[] args) { Func<int, int, int> add = Add; Action<string> print = Print; print(add(1, 2).ToString()); } static int Add(int i, int j) { return i + j; } static void Print(stri

.net mvc lambda表达式Contains方法

Lambda表达式Contains方法(等价于SQL语句中的like)使用注意事项: 众所周知,想在EntityFrame实体框架中使用类似于SQL语句中like的效果时就的使用Contains方法了.可是关于Contains方法使用过程中会出现的细节问题,并没有专门的文章来指出来. 1.使用Contains方法的必备条件: Contains等价于SQL中的like语句.不过Contains只针对于字符串(string)类型的数据而言.如果是int等数值类型,则不会有Contains方法的存在,

Lambda表达式之方法的引用

以下都是示例 public class Syntax3 { public static void main(String[] args) { //方法引用 //可以快速的将一个lambda表达式的实现指向一个已经实现的方法. //语法: 方法的隶属者 :: 方法名 //注意: //1. 参数数量和类型一定要和接口中定义的方法一致 //2. 返回值的类型也要和接口定义的方法一致 LambdaSingleReturnSingleParameter lambda = a -> change(a); l

java8之lambda表达式(构造器引用)

构造器引用同方法引用类似,不同的是在构造器引用中方法名是new.例如,Button::new表示Button类的构造器引用.对于拥有多个构造器的类,选择使用哪个构造器取决于上下文.假设你有一个字符串列表,并且希望调用Button类的构造器使用列表中的字符串来构造一个按钮列表,可以使用如下表达式: List<String> labels = ....; Stream<Button> stream = labels.stream().map(Button::new); List<