当前位置:   article > 正文

12函数式接口、方法引用

方法引用是必须函数式接口才可以用吗

12.1  函数式接口

 

12.1.1 概念

1.函数式接口在Java中是指:有且仅有一个抽象方法的接口。
2.函数式接口,即适用于函数式编程场景的接口,而Java中的函数式编程体现就是Lambda,所有函数式接口就是可以适用于Lamnda使用的接口,只有确保接口中有且仅有一个抽象方法,java中的Lambda才能顺利地进行推导。
3、语法糖的定义:是指使用更加方便,但是原理不变的代码语法,例如在遍历集合时使用的for-each语法,其实底层的实现原理 仍然是迭代器,这就是语法糖,从应用层讲, java中的Lambda可以被当做是匿名内部类的语法糖,但是二者原理上不同的
4.复习之前Lambda的使用要求,使用条件1:必须拥有函数式接口,(java语言中已经提供了很多函数式接口)
                                                     使用条件2:调用的方法必须拥有函数式接口作为方法的参数,(Java语言已经提供了很多方法,这些方法的参数都是函数式接口)
 

12.1.2 格式

只要确保接口中有且仅有一个抽象方法即可
  1. 修饰符 interface 接口名称 {
  2. public abstract 返回值类型 方法名称(可选参数信息);
  3. // 其他非抽象方法内容
  4. }
 
 
 
4
 
 
 
 
 
1
修饰符 interface 接口名称 {
2
    public abstract 返回值类型 方法名称(可选参数信息);
3
    // 其他非抽象方法内容
4
}
 
 
由于接口当中抽象方法的public abstract是可以省略的,所以定义一个函数式接口很简单:
  1. public interface MyFunctionalInterface {
  2. void myMethod();
  3. }
 
 
 
3
 
 
 
 
 
1
public interface MyFunctionalInterface {
2
void myMethod();
3
}
 
 
 

12.1.3 @FunctionalInterface注解

与@Override注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface。该注解可用于一个接口的定义上:
  1. // 函数式接口注解 : 该接口仅能拥有一个抽象方法
  2. @FunctionalInterface
  3. public interface MyFunctionalInterface {
  4. // 抽象方法定义 :
  5. void myMethod();
  6. }
 
 
 
7
 
 
 
 
 
1
// 函数式接口注解 : 该接口仅能拥有一个抽象方法
2
@FunctionalInterface
3
public interface MyFunctionalInterface {
4
 
          
5
    // 抽象方法定义 :
6
    void myMethod();
7
}
 
 
notes:一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
 

12.2  函数式编程

 
 

  12.2.1 Lambda 的延时执行

            有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。
   性能浪费的日志案例:
             一种典型的场景就是对参数进行有条件使用,例如对日志消息进行拼接后,在满足条件的情况下进行打印输出:
  1. public static void main(String[] args) {
  2. String s1 = "Hello";
  3. String s2 = "World";
  4. String s3 = "Java";
  5. // 调用方法
  6. log(0, s1 + s2 + s3);
  7. }
  8. // 方法 :
  9. public static void log(int level, String msg) {
  10. if (level == 1) {
  11. System.out.println(msg);
  12. }
  13. }
  14. 结果:
  15. 说明 : 如果 level 不为 1, 则没有任何输出结果.
  16. 说明 : 如果 level 为 1, 输出结果如下 :
  17. HelloWorldJava
 
 
 
21
 
 
 
 
 
1
    public static void main(String[] args) {
2
 
          
3
        String s1 = "Hello";
4
        String s2 = "World";
5
        String s3 = "Java";
6
 
          
7
        // 调用方法
8
        log(0, s1 + s2 + s3);
9
    }
10
 
          
11
    // 方法 :
12
    public static void log(int level, String msg) {
13
        if (level == 1) {
14
            System.out.println(msg);
15
        }
16
    }
17
 
          
18
结果: 
19
    说明 : 如果 level 不为 1, 则没有任何输出结果.
20
    说明 : 如果 level  1, 输出结果如下 :
21
    HelloWorldJava
 
 
notes:这段代码存在问题:无论级别是否满足要求,作为log方法的第二个参数,三个字符串一定会首先被拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。
 
体验Lambda的优化写法
使用Lambda必然需要一个函数式接口:
  1. @FunctionalInterface
  2. public interface MessageBuilder {
  3. // 抽象方法
  4. String buildMsg();
  5. }
 
 
 
6
 
 
 
 
 
1
@FunctionalInterface
2
public interface MessageBuilder {
3
 
          
4
    // 抽象方法
5
    String buildMsg();
6
}
 
 
然后对log方法进行改造
  1. public static void main(String[] args) {
  2. String s1 = "Hello";
  3. String s2 = "World";
  4. String s3 = "Java";
  5. // 调用方法 :
  6. log(1, () -> s1 + s2 + s3);
  7. }
  8. // 方法 :
  9. public static void log(int level, MessageBuilder messageBuilder) {
  10. if (level == 1) {
  11. String result = messageBuilder.buildMsg();
  12. System.out.println("result = " + result);
  13. }
  14. }
  15. 输出结果 :
  16. result = HelloWorldJava
 
 
 
21
 
 
 
 
 
1
    public static void main(String[] args) {
2
 
          
3
        String s1 = "Hello";
4
        String s2 = "World";
5
        String s3 = "Java";
6
 
          
7
        // 调用方法 :
8
        log(1, () -> s1 + s2 + s3);
9
    }
10
 
          
11
    // 方法 :
12
    public static void log(int level, MessageBuilder messageBuilder) {
13
        if (level == 1) {
14
            String result = messageBuilder.buildMsg();
15
            System.out.println("result = " + result);
16
        }
17
    }
18
 
          
19
 
          
20
输出结果 :
21
result = HelloWorldJava
 
 
noets:这样一来,只有当级别满足要求的时候,才会进行三个字符串的拼接;否则三个字符串将不会进行拼接
从结果中可以看出,在不符合级别要求的情况下,Lambda将不会执行。从而达到节省性能的效果。
 
扩展:实际上使用内部类也可以达到同样的效果,只是将代码操作延迟到了另外一个对象当中通过调用方法来完成。而是否调用其所在方法是在条件判断之后才执行的。
除此之外Lambda还可作为参数和返回值
 

         12.2.2 使用函数式接口作为参数

                    例子:自定义一个函数是接口MyFunctionalInterface,里面有且仅有一个抽象方法void myMethod()。来说明使用函数式接口作为参数
    
                思路历程:
                        Lambda表达式实现                        
                        思考1,接口MyFunctionalInterface中的抽象方法长什么样(有无参数,有无返回值),
                        思考2,Lambda的表达式的各个部分是什么样的 () -> {} 
                                     ()  其中这里面放的是参数列表,一定要和函数式接口中的抽象放的参数列表保持一致
                                      {} 方法的实现题部分        一定要和函数式接口中的抽象方法的返回值保持一致
                        开始写代码:
          
  1. public class MyFunctionalInterfaceTest1 {
  2. public static void main(String[] args) {
  3. // 重点 : 调用方法, 传递 Lambda 表达式作为函数式接口的实际参数
  4. // 思考 : MyFunctionalInterface 函数式接口中的抽象方法长什么样 ??? void myMethod();
  5. // () 小括号 : 函数式接口抽象方法的参数列表.
  6. // {} 大括号 : 函数式接口抽象方法的方法实现体.
  7. // Lambda 表达式的标准格式 :
  8. doSomething(() -> { System.out.println("Lambda 表达式被执行 ..."); });
  9. // Lambda 表达式的省略格式 :
  10. doSomething(() -> System.out.println("Lambda 表达式被执行 ..."));
  11. }
  12. // 定义一个方法, 使用函数式接口作为方法的参数列表
  13. public static void doSomething(MyFunctionalInterface inter) {
  14. // 此处为 Lambda 表达式代码的调用
  15. inter.myMethod();
  16. }
  17. }
 
 
 
20
 
 
 
 
 
1
public class MyFunctionalInterfaceTest1 {
2
    public static void main(String[] args) {
3
 
          
4
        // 重点 : 调用方法, 传递 Lambda 表达式作为函数式接口的实际参数
5
        // 思考 : MyFunctionalInterface 函数式接口中的抽象方法长什么样 ???   void myMethod();
6
        // () 小括号 : 函数式接口抽象方法的参数列表.
7
        // {} 大括号 : 函数式接口抽象方法的方法实现体.
8
        // Lambda 表达式的标准格式 :
9
        doSomething(() -> { System.out.println("Lambda 表达式被执行 ..."); });
10
 
          
11
        // Lambda 表达式的省略格式 :
12
        doSomething(() -> System.out.println("Lambda 表达式被执行 ..."));
13
    }
14
 
          
15
    // 定义一个方法, 使用函数式接口作为方法的参数列表
16
    public static void doSomething(MyFunctionalInterface inter) {
17
        // 此处为 Lambda 表达式代码的调用
18
        inter.myMethod();
19
    }
20
}
 
 
  1. /*
  2. Lambda 表达式书写的前提要求 : (传递 Lambda 表达式作为方法的参数就是重要)
  3. 1. 必须要有 `函数式接口`. (自动推导) (Java提供)
  4. 2. 必须有方法使用 `函数式接口` 作为方法的参数列表. (Java提供)
  5. */
  6. // 请问 : 如何让编译器该接口是否为函数式接口
  7. @FunctionalInterface
  8. public interface MyFunctionalInterface {
  9. // 抽象方法 : 无参无返回值
  10. void myMethod();
  11. }
 
 
 
14
 
 
 
 
 
1
/*
2
Lambda 表达式书写的前提要求 :  (传递 Lambda 表达式作为方法的参数就是重要)
3
 
          
4
    1. 必须要有 `函数式接口`. (自动推导)                         (Java提供)
5
    2. 必须有方法使用 `函数式接口` 作为方法的参数列表.            (Java提供)
6
 */
7
 
          
8
// 请问 : 如何让编译器该接口是否为函数式接口
9
@FunctionalInterface
10
public interface MyFunctionalInterface {
11
 
          
12
    // 抽象方法 : 无参无返回值
13
    void myMethod();
14
}
 
 
 

         12.2.3 使用函数式接口作为返回值

                        例子:自定义一个MySupplier函数式接口,无参有返回值,里面有且仅有一个方法Object getData();来说明函数式接口作为返回值
                     思路历程:
                            1.函数式接口中的抽象方法是什么样子的(Object getData()) 其中 抽象方式是没有参数,但是是有返回值的,返回值类型是 Object,
                            2. Lambda 的格式按照需求应该怎么写
                                    ()中不需要写任何参数列表
                                       {} 中有返回值 加return 书写要实现的方法。
                    开始写代码
  1. public class MySupplierTest1 {
  2. public static void main(String[] args) {
  3. // 调用方法
  4. // 思考 : 看一下 MySupplier 的抽象方法长什么样 ??? Object data();
  5. printData(() -> { return "Hello ShangHai, I Love you."; });
  6. // 调用方法
  7. MySupplier mySupplier = getDataFromLambda();
  8. // 请问 : 如何取出数据 ??? 调用函数式接口中的方法, 来获取数据
  9. Object data = mySupplier.getData();
  10. System.out.println("data = " + data);
  11. }
  12. // 方法 : 将函数式接口作为方法的参数
  13. public static void printData(MySupplier mySupplier) {
  14. Object data = mySupplier.getData();
  15. System.out.println("data = " + data);
  16. }
  17. // 方法 : 将函数式接口作为方法的返回值
  18. public static MySupplier getDataFromLambda() {
  19. // 如何实现 : 返回一个 Lambda 表达式 Object getData();
  20. // return () -> { return "你爱北京长城."; };
  21. return () -> "你爱北京长城.";
  22. }
  23. }
 
 
 
28
 
 
 
 
 
1
public class MySupplierTest1 {
2
    public static void main(String[] args) {
3
 
          
4
        // 调用方法
5
        // 思考 : 看一下 MySupplier 的抽象方法长什么样 ???        Object data();
6
        printData(() -> { return "Hello ShangHai, I Love you."; });
7
 
          
8
        // 调用方法
9
        MySupplier mySupplier = getDataFromLambda();
10
        // 请问 : 如何取出数据 ??? 调用函数式接口中的方法, 来获取数据
11
        Object data = mySupplier.getData();
12
        System.out.println("data = " + data);
13
    }
14
 
          
15
    // 方法 : 将函数式接口作为方法的参数
16
    public static void printData(MySupplier mySupplier) {
17
        Object data = mySupplier.getData();
18
        System.out.println("data = " + data);
19
    }
20
 
          
21
    // 方法 : 将函数式接口作为方法的返回值
22
    public static MySupplier getDataFromLambda() {
23
 
          
24
        // 如何实现 : 返回一个 Lambda 表达式       Object getData();
25
        // return () -> { return "你爱北京长城."; };
26
        return () -> "你爱北京长城.";
27
    }
28
}
 
 
  1. @FunctionalInterface
  2. public interface MySupplier {
  3. // 抽象方法 : 无参, 有返回值
  4. Object getData();
  5. }
 
 
 
6
 
 
 
 
 
1
@FunctionalInterface
2
public interface MySupplier {
3
 
          
4
    // 抽象方法 : 无参, 有返回值
5
    Object getData();
6
}
 
 
 

12.3  方法引用

         概述 : 什么是方法引用呢 ??? 其实就是 Lambda 表达式的孪生兄弟. 也可以理解为引用一个已经实现了 Lambda 表达式相同功能的方法.
         格式 : 对象::对象方法 System.out::println
 

12.3.2 冗余的Lambda场景

看一个简单的函数式接口以应用Lambda表达式
  1. @FunctionalInterface
  2. public interface Printable<T> {
  3. void print(T t);
  4. }
 
 
 
4
 
 
 
 
 
1
@FunctionalInterface
2
public interface Printable<T> { 
3
    void print(T t);
4
}
 
 
在Printable接口当中唯一的抽象方法print接收一个字符串参数,目的就是为了打印显示它。那么通过Lambda来使用它的代码很简单:
  1. public static void main(String[] args) {
  2. // 调用方法 :
  3. printString("Hello beijing, 你好, 北京.", s -> System.out.println(s));
  4. }
  5. // 方法 :
  6. public static void printString(String str, Printable<String> printable) {
  7. printable.print(str);
  8. }
  9. 输出结果:
  10. Hello beijing, 你好, 北京.
 
 
 
13
 
 
 
 
 
1
    public static void main(String[] args) {
2
 
          
3
        // 调用方法 :
4
        printString("Hello beijing, 你好, 北京.", s -> System.out.println(s));
5
    }
6
 
          
7
    // 方法 :
8
    public static void printString(String str, Printable<String> printable) {
9
        printable.print(str);
10
    }
11
 
          
12
输出结果:
13
    Hello beijing, 你好, 北京.
 
 
分析:这段代码的问题在于,对字符串进行控制台打印输出的操作方案,明明已经有了现成的实现,那就是System.out对象中的println(String)方法。既然Lambda希望做的事情就是调用println(String)方法,那何必自己手动调用呢?这时候引入了其孪生兄弟, 方法引用:
    改进之后的代码
  1. public static void main(String[] args) {
  2. // 调用方法 : Lambda 表达式
  3. printString("Hello beijing, 你好, 北京.", s -> System.out.println(s));
  4. // 调用方法 : 方法引用 -> 对象引用对象方法
  5. PrintStream ps = System.out;
  6. printString("Hello beijing, 你好, 北京.", ps::println);
  7. // 调用方法 : 简化格式
  8. printString("Hello beijing, 你好, 北京.", System.out::println);
  9. }
  10. // 方法 :
  11. public static void printString(String str, Printable<String> printable) {
  12. printable.print(str);
  13. }
  14. 输出结果 :
  15. Hello beijing, 你好, 北京.
  16. Hello beijing, 你好, 北京.
  17. Hello beijing, 你好, 北京.
 
 
 
22
 
 
 
 
 
1
    public static void main(String[] args) {
2
 
          
3
        // 调用方法 : Lambda 表达式
4
        printString("Hello beijing, 你好, 北京.", s -> System.out.println(s));
5
 
          
6
        // 调用方法 : 方法引用 -> 对象引用对象方法
7
        PrintStream ps = System.out;
8
        printString("Hello beijing, 你好, 北京.", ps::println);
9
 
          
10
        // 调用方法 : 简化格式
11
        printString("Hello beijing, 你好, 北京.", System.out::println);
12
    }
13
 
          
14
    // 方法 :
15
    public static void printString(String str, Printable<String> printable) {
16
        printable.print(str);
17
    }
18
 
          
19
输出结果 :
20
    Hello beijing, 你好, 北京.
21
    Hello beijing, 你好, 北京.
22
    Hello beijing, 你好, 北京.
 
 
notes:
            注意其中的双冒号::写法,这被称为“方法引用”,而双冒号是一种新的语法。
 

12.3.3 方法引用符

双冒号::为引用运算符 ,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。
 
语义分析
 
例如上例中,System.out对象中有一个重载的println(String)方法恰好就是我们所需要的。那么对于printString方法的函数式接口参数,对比下面两种写法,完全等效:
 
- Lambda表达式写法:s -> System.out.println(s);
- 方法引用写法:System.out::println
 
第一种语义是指:拿到参数之后经Lambda之手,继而传递给System.out.println方法去处理。
 
第二种等效写法的语义是指:直接让System.out中的println方法来取代Lambda。两种写法的执行效果完全一样,而第二种方法引用的写法复用了已有方案,更加简洁。
 
推导与省略
 
如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式——它们都将被自动推导。而如果使用方法引用,也是同样可以根据上下文进行推导。
 
函数式接口是Lambda的基础,而方法引用是Lambda的孪生兄弟。
 
 

12.3.4 通过对象名引用成员方法

           这是最常见的一种用法,与上例相同。如果一个类中已经存在了一个成员方法:
           思路历程:
                1. 接口Printable 中的抽象方法 void print(T t)  有参数 未指定类型,无返回值 
                printString("Hello World. 你好, 世界!", s -> System.out.println(s));
 
                2,
                        上面传递的Lambda 表达式 (s -> System.out,println(s) )的实现效果与 Sysetm.out 对象的println 方法实现一致
                        既然效果一样,那么Lambda表达式可不可以不写了,直接引用别人的方法。
                    方法引用的条件:引用的方法必须与函数式中的抽象方法匹配,(参数类型和返回值需要保持一致)
                      // Printable : void print(T t); System.out 对象的 void println(String x);
                      // 引用语法 : 对象名::对象方法名
                3.
                    // 简化格式 : System.out::println 对象名::对象方法
                    // 作用 : 已经有方法完成 Lambda 表达式需要实现的效果. 因此, 在程序中可以使用方法引用替代 Lambda 表达式.
                4.
                    // 方法 : 使用函数式接口作为参数 (这是 Lambda 表达式的传递基本)
 
  1. public class ObjectMethodRef {
  2. // 对象方法 :
  3. public void printUpperCase(String str) {
  4. String s = str.toUpperCase();
  5. System.out.println(s);
  6. }
  7. }
 
 
 
8
 
 
 
 
 
1
public class ObjectMethodRef {
2
 
          
3
    // 对象方法 :
4
    public void printUpperCase(String str) {
5
        String s = str.toUpperCase();
6
        System.out.println(s);
7
    }
8
}
 
 
  1. // 函数式接口仍然定义为
  2. @FunctionalInterface
  3. public interface Printable<T> {
  4. void print(T t);
  5. }
 
 
 
5
 
 
 
 
 
1
// 函数式接口仍然定义为
2
@FunctionalInterface
3
public interface Printable<T> {
4
    void print(T t);
5
}
 
 
那么当需要使用这个printUpperCase成员方法来替代Printable接口的Lambda的时候,已经具有了ObjectMethodRef类的对象实例,则可以通过对象名引用成员方法,代码为:
  1. public static void main(String[] args) {
  2. // Lambda 表达式实现 :
  3. printString("hello", s -> System.out.println(s.toUpperCase()));
  4. // 对象方法引用 :
  5. ObjectMethodRef obj = new ObjectMethodRef();
  6. printString("hello", obj::printUpperCase);
  7. }
  8. public static void printString(String str, Printable<String> printable) {
  9. printable.print(str);
  10. }
  11. 输出结果 :
  12. HELLO
  13. HELLO
 
 
 
17
 
 
 
 
 
1
    public static void main(String[] args) {
2
 
          
3
        // Lambda 表达式实现 :
4
        printString("hello", s -> System.out.println(s.toUpperCase()));
5
 
          
6
        // 对象方法引用 :
7
        ObjectMethodRef obj = new ObjectMethodRef();
8
        printString("hello", obj::printUpperCase);
9
    }
10
 
          
11
    public static void printString(String str, Printable<String> printable) {
12
        printable.print(str);
13
    }
14
 
          
15
输出结果 :
16
HELLO
17
HELLO
 
 
 

12.3.5 通过类名称引用静态方法

由于在java.lang.Math类中已经存在了静态方法abs,所以当我们需要通过Lambda来调用该方法时,有两种写法。首先是函数式接口:
  1. @FunctionalInterface
  2. public interface Calculator {
  3. // 抽象方法 :
  4. int calc(int num);
  5. }
 
 
 
6
 
 
 
 
 
1
@FunctionalInterface
2
public interface Calculator {
3
 
          
4
    // 抽象方法 :
5
    int calc(int num);
6
}
 
 
  1. public class Test3 {
  2. public static void main(String[] args) {
  3. // 调用方法 : Lambda 表达式
  4. printCalculator(-88, num -> Math.abs(num));
  5. // 调用方法 : 静态方法引用
  6. printCalculator(-88, Math::abs);
  7. }
  8. // 方法 :
  9. public static void printCalculator(int num, Calculator calculator) {
  10. int result = calculator.calc(num);
  11. System.out.println("result = " + result);
  12. }
  13. }
  14. 输出结果 :
  15. result = 88
  16. result = 88
 
 
 
20
 
 
 
 
 
1
public class Test3 {
2
    public static void main(String[] args) {
3
 
          
4
        // 调用方法 : Lambda 表达式
5
        printCalculator(-88, num -> Math.abs(num));
6
 
          
7
        // 调用方法 : 静态方法引用
8
        printCalculator(-88, Math::abs);
9
    }
10
 
          
11
    // 方法 :
12
    public static void printCalculator(int num, Calculator calculator) {
13
        int result = calculator.calc(num);
14
        System.out.println("result = " + result);
15
    }
16
}
17
 
          
18
输出结果 :
19
result = 88
20
result = 88
 
 
notes:
    在这个例子中,下面两种写法是等效的:
 
- Lambda表达式:n -> Math.abs(n)
- 方法引用:Math::abs
 

12.3.6 通过super引用成员方法

如果存在继承关系,当Lambda中需要出现super调用时,也可以使用方法引用进行替代。首先是函数式接口
  1. @FunctionalInterface
  2. public interface Greetable {
  3. void greet();
  4. }
 
 
 
4
 
 
 
 
 
1
@FunctionalInterface
2
public interface Greetable {
3
  void greet();
4
}
 
 
  1. public class Human {
  2. public void sayHello() {
  3. System.out.println("Hello!");
  4. }
  5. }
 
 
 
5
 
 
 
 
 
1
public class Human {
2
    public void sayHello() {
3
      System.out.println("Hello!");
4
    }
5
}
 
 
  1. pubpublic class Man extends Human {
  2. public void sayHi() {
  3. // 调用方法 : Lambda 表达式
  4. method(() -> System.out.println("Hello!"));
  5. // 调用方法 : 执行父类中的 sayHello 方法.
  6. method(() -> super.sayHello());
  7. // 调用方法 : super引用, 使用父类中的 sayHello 方法.
  8. method(super::sayHello);
  9. }
  10. public void method(Greeting greeting) {
  11. greeting.greet();
  12. System.out.println("I am a Man.");
  13. }
  14. }
 
 
 
20
 
 
 
 
 
1
pubpublic class Man extends Human {
2
 
          
3
    public void sayHi() {
4
 
          
5
        // 调用方法 : Lambda 表达式
6
        method(() -> System.out.println("Hello!"));
7
        
8
        // 调用方法 : 执行父类中的 sayHello 方法.
9
        method(() -> super.sayHello());
10
 
          
11
        // 调用方法 : super引用, 使用父类中的 sayHello 方法.
12
        method(super::sayHello);
13
    }
14
 
          
15
    public void method(Greeting greeting) {
16
        greeting.greet();
17
 
          
18
        System.out.println("I am a Man.");
19
    }
20
}
 
 
notes:
 在这个例子中,下面两种写法是等效的:
 
- Lambda表达式:() -> super.sayHello()
- 方法引用:super::sayHello
 

12.3.7 通过this引用成员方法

this代表当前对象,如果需要引用的方法就是当前类中的成员方法,那么可以使用“this::成员方法”的格式来使用方法引用。首先是简单的函数式接口:
  1. @FunctionalInterface
  2. public interface Richable {
  3. void buy();
  4. }
 
 
 
4
 
 
 
 
 
1
@FunctionalInterface
2
public interface Richable {
3
    void buy();
4
}
 
 
  1. public class Husband {
  2. // 行为 : 变得快乐
  3. public void beHappy() {
  4. // 结婚吧
  5. merry(() -> System.out.println("买套房子."));
  6. merry(() -> this.buyCar());
  7. merry(this::changeWife);
  8. }
  9. // 行为 : 结婚 (需要变得有钱, 必须要买东西)
  10. private void merry(Richable richable) {
  11. richable.buy();
  12. }
  13. // 行为 : 买套方法
  14. private void buyHouse() {
  15. System.out.println("买套房子.");
  16. }
  17. // 行为 : 买辆车子
  18. private void buyCar() {
  19. System.out.println("买辆车子.");
  20. }
  21. }
 
 
 
27
 
 
 
 
 
1
public class Husband {
2
 
          
3
    // 行为 : 变得快乐
4
    public void beHappy() {
5
        // 结婚吧
6
        merry(() -> System.out.println("买套房子."));
7
 
          
8
        merry(() -> this.buyCar());
9
 
          
10
        merry(this::changeWife);
11
    }
12
 
          
13
    // 行为 : 结婚 (需要变得有钱, 必须要买东西)
14
    private void merry(Richable richable) {
15
        richable.buy();
16
    }
17
 
          
18
    // 行为 : 买套方法
19
    private void buyHouse() {
20
        System.out.println("买套房子.");
21
    }
22
 
          
23
    // 行为 : 买辆车子
24
    private void buyCar() {
25
        System.out.println("买辆车子.");
26
    }
27
}
 
 
  1. public class Test {
  2. public static void main(String[] args) {
  3. Husband husband = new Husband();
  4. husband.beHappy();
  5. }
  6. }
  7. 输出结果 :
  8. 买套房子.
  9. 买辆车子.
 
 
 
12
 
 
 
 
 
1
public class Test {
2
    public static void main(String[] args) {
3
 
          
4
        Husband husband = new Husband();
5
        husband.beHappy();
6
 
          
7
    }
8
}
9
 
          
10
输出结果 :
11
买套房子.
12
买辆车子.
 
 
开心方法beHappy调用了结婚方法marry,后者的参数为函数式接口Richable:
 
在这个例子中,下面两种写法是等效的:
 
- Lambda表达式:() -> this.buyCar()
- 方法引用:this::buyCar
 

12.3.8 类的构造器引用

由于构造器的名称与类名完全一样,并不固定。所以构造器引用使用类名称::new的格式表示。首先是一个简单的Person类:
  1. public class Person {
  2. private String name;
  3. public Person(String name) {
  4. this.name = name;
  5. }
  6. public Person() {
  7. }
  8. public String getName() {
  9. return name;
  10. }
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14. }
 
 
 
19
 
 
 
 
 
1
public class Person {
2
    
3
    private String name;
4
 
          
5
    public Person(String name) {
6
        this.name = name;
7
    }
8
 
          
9
    public Person() {
10
    }
11
 
          
12
    public String getName() {
13
        return name;
14
    }
15
 
          
16
    public void setName(String name) {
17
        this.name = name;
18
    }
19
}
 
 
在这个例子中,下面两种写法是等效的:
 
- Lambda表达式:name -> new Person(name)
- 方法引用:Person::new
 

12.3.9 数组的构造器引用

数组也是Object的子类对象,所以同样具有构造器,只是语法稍有不同。
需要一个函数式接口:
  1. @FunctionalInterface
  2. public interface ArrayBuilder {
  3. // 抽象方法
  4. int[] buildArray(int length);
  5. }
 
 
 
5
 
 
 
 
 
1
@FunctionalInterface
2
public interface ArrayBuilder {
3
    // 抽象方法
4
    int[] buildArray(int length);
5
}
 
 
  1. public class Test2 {
  2. public static void main(String[] args) {
  3. int[] arr1 = initIntArray(10, len -> new int[len]);
  4. System.out.println("arr1.length = " + arr1.length);
  5. int[] arr2 = initIntArray(10, int[]::new);
  6. System.out.println("arr2.length = " + arr2.length);
  7. }
  8. // 初始化一个 int[] 数组
  9. public static int[] initIntArray(int length, ArrayBuilder builder) {
  10. return builder.buildArray(length);
  11. }
  12. }
  13. 输出结果 :
  14. arr1.length = 10
  15. arr2.length = 10
 
 
 
19
 
 
 
 
 
1
public class Test2 {
2
    public static void main(String[] args) {
3
 
          
4
        int[] arr1 = initIntArray(10, len -> new int[len]);
5
        System.out.println("arr1.length = " + arr1.length);
6
 
          
7
        int[] arr2 = initIntArray(10, int[]::new);
8
        System.out.println("arr2.length = " + arr2.length);
9
    }
10
 
          
11
    // 初始化一个 int[] 数组
12
    public static int[] initIntArray(int length, ArrayBuilder builder) {
13
       return builder.buildArray(length);
14
    }
15
}
16
 
          
17
输出结果 :
18
arr1.length = 10
19
arr2.length = 10
 
 
在这个例子中,下面两种写法是等效的:
 
- Lambda表达式:length -> new int[length]
- 方法引用:int[]::new
 

12.4 总结之 前瞻 Java语言的4大核心函数式接口

 
 
 

12.4.1 总结 

        第一步:定义函数式接口
        第二步: 定义方法,将函数式接口作为方法的参数类型
        
        总结:只要满足前两个步骤,我们就可以书写Lambda表达式了,
       问:    在使用Lambda表达式传送传输时,思考哪两个东西??
                思考1:函数式接口中华抽象方法的参数列表
                思考2:函数式接口中抽象方法的返回值类型
        核心点:要求程序员对函数式接口中的抽象方法,参数类型和返回值类型,非常清晰的理解。因此没有这一层的理解,Lambda表达式的语法就无从书写了
 
        Lambda表达式的语法格式:(抽象方法参数) ->  { return  抽象方法的实现体 }
 

12.4.2 4大函数式接口

        Java 语言中提供了最重要的  "四大核心函数式接口"                    特有的抽象方法
         1、消费型接口:                    特点(有去无回)                        
                                                         Consumer<T>                             void accept(T  t)    
        2、供给型接口                         特点 (无中生有)
                                                        Supplier<T>                                  T  get();
        3、函数型接口                         特点(有去有回)
                                                         Function<T, R>                            R apply (T  t)                T -> Type 参数类型  R -> Result 返回值类型
        4、断言型接口                        特点(元芳,你怎么看)
                                                        Predicate<T>                                boolean  test (T  t)
 
说明:使用函数式接口作为方法的参数,这些API大部分都被定义到  StreamAPI  
        StreamAPI  主要操作对象为 “集合” 对象,不是  “读写”  对象
        说明1:读写IO  主要是    “内存与硬盘”    实现数据交互
        说明2:StreamAPI     主要是    ”内存与内存“        实现数据交互    (变量,数组,集合)
 
 
 
 
 
 
 
 
 

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

 
 
 
 





转载于:https://www.cnblogs.com/zhengyuan/p/9362115.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/590674
推荐阅读
相关标签
  

闽ICP备14008679号