Java Review (十六、面向对象----Lambda 表达式)

简介: Java Review (十六、面向对象----Lambda 表达式)

   

文章目录

Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。

Lambda表达式入门

用匿名内部类实现命令模式

 Command接口

public interface Command {
   //接口里定义的方法相当于处理封装行为
  int progress(int[] target);
}

ProgressArray.java

public class ProgressArray {
  public ProgressArray() {    
  } 
  public void progress(int[] target,Command  cmd) {
    cmd.progress(target);
  }
}

CommandInnerTest.java

public class CommandInnerTest {
  public CommandInnerTest() {
  }
  public static void main(String[] args) {
  ProgressArray progressArray=new ProgressArray();
  int[] array= new int[] {3, -4, 6, 4} ;
  //处理数组,具体处理行为取决于匿名内部类
  progressArray.progress(array, new Command() {
    @Override
    public void progress(int[] target) {
      int sum=0;
      int[] a=target;
      for(int j=0;j<a.length;j++ ) {
         int temp=a[j];
         sum+=temp;
      }
           System.out.println("数组元素的总和:"+sum);
    }
  });
}
}

Lambda表示式实现命令模式

Lambda 表达式可用于简化创建匿名内部类对象:

CommandLambdaTest.java

public class CommandLambdaTest {
  public static void main(String[] args) {
    ProgressArray progressArray = new ProgressArray();
    int[] array = { 3, -4, 6, 4 };
    // 处理数组,具体行为取决于匿名内部类
    progressArray.progress(array, (int[] target) -> {
      int sum = 0;
      for (int temp : array) {
        sum += temp;
      }
      System.out.println("数组元素总和为" + sum);
    });
  }
}

当使用 Lambda 表达式代替匿名内部类创建对象时, Lambda 表达式的代码

块将会代替实现抽象方法的方法体, Lambda 表达式就相当一个匿名方法。

从上面语法格式可以看出, Lambda 表达式的主要作用就是代替匿名内部类的烦琐语法。它分三部分组成。

  • 形参列表 。形参列表允许省略形参类型 。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。
  • 箭头 —>。必须通过英文中画线和大于符号组成。
  • 代码块。如果代码块只包含一条语句, Lambda 表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束。 Lambda 码块只有一条 return语句,甚至可以省略return关键字, Lambda 表达式会自动返回这条语句的值。

Lambda语法简单实例

public class LambdaTest {
  public static void main(String[] args) {
    LambdaTest tester=new LambdaTest();
    // 类型声明
    MathOperation addition = (int a, int b) -> a + b;
    // 不用类型声明
    MathOperation subtraction = (a, b) -> a - b;
    // 大括号中的返回语句
    MathOperation multiplication = (int a, int b) -> {
      return a * b;
    };
    // 没有大括号及返回语句
    MathOperation division = (int a, int b) -> a / b;
    System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
    System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
    System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
    System.out.println("10 / 5 = " + tester.operate(10, 5, division));
    // 不用括号
    GreetingService greetService1 = message -> System.out.println("Hello " + message);
    // 用括号
    GreetingService greetService2 = (message) -> System.out.println("Hello " + message);
    greetService1.sayMessage("Runoob");
    greetService2.sayMessage("Google");
  }
  interface MathOperation {
    int operation(int a, int b);
  }
  interface GreetingService {
    void sayMessage(String message);
  }
  private int operate(int a, int b, MathOperation mathOperation) {
    return mathOperation.operation(a, b);
  }
}

Lambda 表达式与函数式接口

Lambda 表达式的类型,也被称为"目标类型( target type) ", Lambda 表达式的目标类型必须是"函数式接口(  functional interface ) "。 函数式接口代表只包含一个抽象方法的接口 。  函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法 。

如果采用匿名内部类语法来创建函数式接口的实例,则只需要实现一个抽象方法,在这种情况下即可采用 Lambda  表达式来创建对象,该表达式创建出来的对象的目标类型就是这个函数式接口 。 查询 Java 8的 API 文档,可以发现大量的函数式接口,例如:  Runnable、 ActionListener 等接口都是函数式接口 。

Java 8专门为函数式接口提供了 @FunctionalInterface 注解,该注解通常放在接口定义前面,该注解对程序功能没有任何作用,它用于告诉编译器执行更严格检查一一检查该接口必须是函数式接口 ,否则编译器就会报错。

由于 Lambda 表达式的结果就是被当成对象 , 因此程序中完全可以使用 Lambda 表达式进行赋值,

例如如下代码 :

// Runnable 接口中只包含一个无参数的方法
// Lambda 表达式代表的匿名方法实现了 Runnable 接口中唯一的、无参数的方法
// 因此下面的 Lambda 表达式创建了 一个 Runnable 对象
Runnable r = () -> {
   for(int i = 0 ; i < 100 ; i ++){
   System.out.println() ;
 }
} 

Lambda 表达式实现的是匿名方法,因此它只能实现特定函数式接口中的唯一方法 。 这意味着 Lambda 表达式有如下两个限制 :

  • Lambda 表达式的目标类型必须是明确的函数式接口 。
  • Lambda 表达式只能为函数式接口创建对象 。 Lambda 表达式只能实现一个方法 , 因此它只能为只有一个抽象方法的接口(函数式接口)创建对象 。

为了保证 Lambda 表达式的目标类型是一个明确的函数式接口,可以有如下三种常见方式 。

  • 将 Lambda 表达式赋值给函数式接口类型的变量 。
  • 将 Lambda 表达式作为函数式接口类型的参数传给某个方法 。
  • 使用函数式接口对 Lambda 表达式进行强制类型转换。
Object obj1 = (Runnable) () - > {
  for(int i = 0 ; i < 100 ; i ++{
   System.out.println(i);
  }   

方法引用与构造器引用

如果 Lambda 表达式的代码块只有一条代码,程序就可以省略Lambda 表达式中

代码块的花括号 。 不仅如此,如果 Lambda 表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用 。

方法引用和构造器引用可以让 Lambda 表达式的代码块更加简洁 。 方法引用和构造器引用都需要使用两个英文冒号。

Lambda 表达式支持的方法引用和构造器引用

image.png

引用类方法

//函数式接口
@FunctionalInterface
interface Converter{
//将 String 参数转换为Integer
  Integer convert(String from);
}
// 下面代码使用 Larnbda 表达式创建 Converter 对象
Converter converterl = from -> Integer.valueOf(from);
//调用 converterl 对象的 convertO方法将字符串转换为整数
Integer val = converterl.convert( " 99 " );
System.out.println (val); // 输出整数 99

可以如下替换:

//方法引用代替 Larnbda 表达式:引用类方法
//函数式接口中被实现方法的全部参数传给该类方法作为参数
Converter converterl = Integer::valueOf;

引用特定对象的实例方法

// 下面代码使用 Larnbda 表达式创建 Converter 对象
Converter converter2 = from - > " fkit.org ".indexOf (from) ;
//调用 converterl 对象的 convertO方法将字符串转换为整数
Integer value = converter2.convert( "it "};
System.out.println(value} ;   //输出 2

可以如下替换:

// 方法引用代替 Lambda 表达式 : 引用特定对象 的实例方法
// 函数式接口中被实现方法的全部参数传给该方法作为参数
Converter converter2 = "fkit.org": :indexOf ;

引用某类对象的实例方法

//定义函数式接口
@Functionallnterface
interface MyTest{
  //根据 String 、 int 、 int 三个参数生成一个 String返回值
  String test(String a , int b , int c);
}  
// 下面代码使用 Lambda 表达式创建 MyTest 对象
MyTest mt = (a , b , c) - > a.substring(b , c};
//调用mt的test()方法
String str = mt.test( "Java 1 Love you" , 2 , 9) ;
System.out.println(str} ; // 输出 : va 1 Lo

可以如下替换:

// 方法引用代替 Lambda 表达式: 引 用某类对象 的实例方法
// 函数式接口中被实现方法的第一个参数作为调用者
// 后面的参数全部传给该方法作为参数
MyTest mt = String ::substring;

引用构造器

@Funct ionallnterface
interface YourTest{
//根据 String 参数生成一个 JFrame 返回值
 JFrame win(String title);
// 下面代码使用 Lambda 表达式创建 YourTest 对象
YourTest yt = (String a) - > new JFrame(a);
JFrame jf = yt.win( "我的窗口 ") ;
System.out.println(jf) ;

可以如下替换:

// 构造器引用代替 Lambda 表达式
//函数式接口中被实现方法的全部参数传给该构造器作为参数
YourTest yt = JFrame ::new;

Lambda 表达式与匿名内部类的联系和区别

从前面介绍可以看出, Lambda 表达式是匿名内部类的一种简化 , 因此它可以部分取代匿名内部类的作用。

Lambda 表达式与匿名内部类存在如下相同点 :

  • Lambda 表达式与匿名内部类一样,都可以直接访问 “effectively final” 的局部变量,以及外部类的成员变量(包括实例变量和类变量〉 。
  • Lambda 表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法 。

Lambda 表达式与匿名内部类主要存在如下区别:

  • 匿名内部类可以为任意接口创建实例一一不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可;但 Lambda 表达式只能为函数式接口创建实例。
  • 匿名内部类可以为抽象类甚至普通类创建实例 ; 但 Lambda 表达式只能为函数式接口创建实例 。
  • 匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法 ; 但 Lambda 表达式的代码块不允许调用接口中定义的默认方法。


参考:

【1】:《疯狂Java讲义》

【2】:https://www.jb51.net/article/171183.htm

【3】:https://www.runoob.com/java/java8-lambda-expressions.html

【4】:《Java核心技术 卷一》


目录
相关文章
|
8天前
|
Java
java中面向过程和面向对象区别?
java中面向过程和面向对象区别?
13 1
|
26天前
|
Java
探索Java中的Lambda表达式
【10月更文挑战第37天】本文将带你深入理解Java的Lambda表达式,从基础语法到高级特性,通过实例讲解其在函数式编程中的应用。我们还将探讨Lambda表达式如何简化代码、提高开发效率,并讨论其在实际项目中的应用。
|
20天前
|
JavaScript 前端开发 Java
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。如果你从我的文章中受益,欢迎关注我,我将持续更新更多优质内容。你的支持是我前进的动力!🎉🎉🎉
21 0
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
|
28天前
|
Java API
Java中的Lambda表达式与函数式编程####
【10月更文挑战第29天】 本文将深入探讨Java中Lambda表达式的实现及其在函数式编程中的应用。通过对比传统方法,我们将揭示Lambda如何简化代码、提高可读性和维护性。文章还将展示一些实际案例,帮助读者更好地理解和应用Lambda表达式。 ####
|
28天前
|
JSON 自然语言处理 Java
这款轻量级 Java 表达式引擎,真不错!
AviatorScript 是一个高性能、轻量级的脚本语言,基于 JVM(包括 Android 平台)。它支持数字、字符串、正则表达式、布尔值等基本类型,以及所有 Java 运算符。主要特性包括函数式编程、大整数和高精度运算、完整的脚本语法、丰富的内置函数和自定义函数支持。适用于规则判断、公式计算、动态脚本控制等场景。
|
1月前
|
Java API 开发者
Java中的Lambda表达式与函数式编程####
在Java的演变过程中,Lambda表达式和函数式编程的引入无疑是一次重大的飞跃。本文将深入探讨Lambda表达式的定义、用法及优势,并结合实例说明如何在Java中利用Lambda表达式进行函数式编程。通过对比传统编程方式,揭示Lambda表达式如何简化代码、提高开发效率和可维护性。 ####
|
13天前
|
安全 Java API
Java中的Lambda表达式与Stream API的高效结合####
探索Java编程中Lambda表达式与Stream API如何携手并进,提升数据处理效率,实现代码简洁性与功能性的双重飞跃。 ####
23 0
|
1月前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
26 2
|
1月前
|
Java API 数据处理
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。
|
Java
QuartZ Cron表达式在java定时框架中的应用
CronTrigger CronTriggers往往比SimpleTrigger更有用,如果您需要基于日历的概念,而非SimpleTrigger完全指定的时间间隔,复发的发射工作的时间表。 CronTrigger,你可以指定触发的时间表如“每星期五中午”,或“每个工作日9:30时”,甚至“每5分钟一班9:00和10:00逢星期一上午,星期三星期五“。
1103 0