前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java面向对象之函数式编程

Java面向对象之函数式编程

作者头像
头发还在
发布2023-10-16 10:59:27
2070
发布2023-10-16 10:59:27
举报
文章被收录于专栏:桃花源

1 函数式编程

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“用什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式来做。

1.1 做什么,而不是怎么做

  • 例如:
代码语言:javascript
复制
new Thread(new Runnable(){
    @Override
    public void run(){
        //TODO things
    }
}).start();

对于 ​​Runnable​​ 的匿名内部类用法,可以分析出几点内容:

  • ​Thread​​​ 类需要 ​​Runnable​​ 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心;
  • 为了指定 run 的方法体,我们必须要创建 ​​Runnable​​ 接口的实现类;
  • 为了省去定义一个 ​​Runnable​​ 实现类的麻烦,我们必须要使用匿名内部类;
  • 在匿名内部类中,我们必须覆盖重写抽象的 run 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能有错;
  • 而实际上,大概仅有 run() 方法体才是程序中最需要的关键所在。
  • 我们真正希望做的事情是,将 run 方法体内的代码传递给 Thread 类并去执行。

思考:

我们真的希望创建一个匿名内部类对象吗?不!

我们只是为了做这件事情而不得不创建一个对象。

1.2 函数式编程的本质是什么?

传递一段代码——这才是我们真正目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。那么,有没有更加简单的方法呢?

如果我们将关注点从“怎么做”回归到“做什么”的本质上,就会发现只要能够更好地达到目的,过程与形式其实并不重要。

匿名内部类存在的一个问题是,即使匿名内部类的实现非常简单,例如只包含一个抽象方法的接口,那么匿名内部类的语法仍然显得比较冗余。

解决方法:可以使用JDK8开始支持的 lambda 表达式,这种表达式只针对有一个抽象方法的接口实现,以简洁的表达式形式实现接口功能来作为方法参数。

代码语言:javascript
复制
new Thread(() -> System.out.println("多线程任务执行!")).start();

这段代码和刚才的执行效果是完全一样的,可以在1.8或更高的编译级别下通过。从代码的语义中可以看出:我们启动了一个线程,而线程任务的内容以一种更加简洁的形式被指定。

不再有“不得不创建接口对象”的束缚,不再有“抽象方法覆盖重写”的负担,就是这么简单!

1.3 Lambda表达式

lambda 表达式的基本语法格式

语句中通过箭头来区分开参数列表和方法体

2 函数式接口

2.1 是否可以使用 lambda 代替所有匿名内部类?

接口中有且只有一个抽象方法时,才能使用 lambda 表达式代替匿名内部类。这是因为 lambda 表达式是基于函数式接口实现的。

所谓函数式接口是指有且只有一个抽象方法的接口,lambda 表达式就是java中函数式编程的体现,只有确保接口中有且只有一个抽象方法,lambda 表达式才能顺利地推导出所实现的这个接口中的方法。

2.2 定义

在JDK8中,接口上标注有 @FunctionalInterface 注解的即为函数式接口,在函数式接口内部有且只有一个抽象方法。

来看下 Runnable 接口中的源码:

说明:@FunctionalInterface 注解只是显式的标注了接口是一个函数式接口,并强制编辑器进行更严格的检查,确保该接口是函数式接口。

JDK8之前已有的部分函数式接口:

  • java.lang.Runnable
  • java.util.Comparater
  • java.io.FileFilter
  • java.lang.Reflect.InvocationHandler

JDK8 增加的函数式接口

java.util.function 包下包含了很多类,用来支持java的函数式编程。

​​​​​​​

2.3 Lambda 表达式的简化

lambda 表达式的省略写法(进一步在lambda表达式的基础上继续简化)

1、如果lambda表达式的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号!

2、如果lambda表达式的方法体代码只有一行代码,如果这行代码是return 语句,必须省略return 不写,同时省略分号。

3、参数类型可以省略不写。

4、如果只有一个参数,参数外的()也可以省略。

除了以上简化规则,还可以使用“方法引用”进一步简化 lambda 表达式。

代码语言:javascript
复制
public class TestOutter {
  public static void main(String[] args) {
    //匿名函数写法
    new Thread(new Runnable() {     
      @Override
      public void run() {
        System.out.println("线程1启动了!");
        
      }
    }).start();
    
    //lambda写法
    new Thread(() -> {System.out.println("线程2启动了!");}).start();
  }
}

3 方法引用

说明:lambda 表达式的主体只有一条语句时,程序不仅可以省略包含主体的大括号,还可以通过英文双冒号“::”的语法格式来引用方法和构造器(即构造方法)。

作用:可以进一步简化lambda表达式的书写,其本质都是对 lambda 表达式的主体部分已存在的方法进行直接引用,主要区别就是对普通方法与构造方法的引用而已。

3.1 类名引用静态方法

定义:类名引用静态方法也就是通过类名对静态方法的引用,该类可以是java自带的特殊类,也可以是自定义的普通类。

引用示例如下:

代码语言:javascript
复制
//定义函数式接口
@FunctionalInterface
interface Calcable{
  int calc(int num);
}

//定义一个类,类中定义一个静态方法
class Math{
  public static int abs(int num){
    return num > 0 ? num : -num;
  }
}

public class Example {
  private static void printAbs(int num, Calcable calcable){
    System.out.println(calcable.calc(num));
  }
  
  public static void main(String[] args) {
    
    //1、使用匿名内部类
    printAbs(-8, new Calcable() {     
      @Override
      public int calc(int num) {
        return Math.abs(num);
      }
    });
    
    //2、使用 lambda 表达式
    printAbs(-8, (num) -> {return Math.abs(num);});
    
    //3、简化 lambda 表达式
    printAbs(-9, num -> Math.abs(num));
    
    //4、使用方法引用,简化 lambda 表达式
    printAbs(-7, Math::abs);
  }
}

3.2 对象名引用方法

定义:对象名引用方法是指通过实例化对象的名称,来对其方法进行的引用。

引用示例如下:

代码语言:javascript
复制
//定义函数式接口
@FunctionalInterface
interface Utils{
  String calc(String str);
}

//定义一个类,类中定义一个普通方法
class UpperUtil{
  public String toUpper(String str){
    return str.toUpperCase();
  }
}

public class Example2 {
  private static void printUpper(String ori, Utils u){
    System.out.println(u.calc(ori));
  }
  
  public static void main(String[] args) {
    UpperUtil upper = new UpperUtil();
    
    //1、使用匿名内部类
    printUpper("abc", new Utils() {     
      @Override
      public String calc(String ori) {
        return upper.toUpper(ori);
      }
    });
    
    //2、使用 lambda 表达式
    printUpper("bcd", (ori) -> {return upper.toUpper(ori);});
    
    //3、简化 lambda 表达式
    printUpper("cde", ori -> upper.toUpper(ori));
    
    //4、使用方法引用,简化 lambda 表达式
    printUpper("def", upper::toUpper);
  }
}

3.3 构造器引用方法

定义:是指对类自带的构造器的引用。

引用示例如下:

代码语言:javascript
复制
//定义函数式接口
@FunctionalInterface
interface PersonBuilder{
  Person buildPerson(String name);
}

//定义一个类,类中定义一个构造器及普通方法
class Person{
  private String name;
  
  public Person(String name){
    this.name = name;
  }
  
  public String getName(){
    return name;
  }
}

public class Example3 {
  private static void printName(String name, PersonBuilder buildPerson){
    System.out.println(buildPerson.buildPerson(name).getName());
  }
  
  public static void main(String[] args) {
    
    //1、使用 lambda 表达式
    printName("李四", (String name) -> {return new Person(name);});
    
    //2、使用构造器引用的方式
    printName("张三", Person::new);
  }
}

3.4 类名引用普通方法

定义:是指通过一个普通类的类名,来对其普通方法进行的引用。

引用示例如下:

代码语言:javascript
复制
//定义函数式接口
@FunctionalInterface
interface Printable{
  void print(StringUtils su, String str);
}

//定义一个类及方法
class StringUtils{  
  public void printUppercase(String str){
    System.out.println(str.toUpperCase());
  }
}

public class Example4 {
  private static void printUpper(StringUtils su, String str, Printable printable){
    printable.print(su, str);
  }
  
  public static void main(String[] args) {
    //1、使用匿名内部类
    printUpper(new StringUtils(), "abc", new Printable() {      
      @Override
      public void print(StringUtils su, String str) {
        su.printUppercase(str);       
      }
    });
    
    //2、使用 lambda 表达式
    printUpper(new StringUtils(), "bcd", (Object, t) -> Object.printUppercase(t));
    
    //3、使用方法引用的方式
    printUpper(new StringUtils(), "def", StringUtils::printUppercase);
  }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 函数式编程
    • 1.1 做什么,而不是怎么做
      • 1.2 函数式编程的本质是什么?
        • 1.3 Lambda表达式
        • 2 函数式接口
          • 2.1 是否可以使用 lambda 代替所有匿名内部类?
            • 2.2 定义
              • 2.3 Lambda 表达式的简化
              • 3 方法引用
                • 3.1 类名引用静态方法
                  • 3.2 对象名引用方法
                    • 3.3 构造器引用方法
                      • 3.4 类名引用普通方法
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档