Java Lambda表达式 匿名内部类 函数式接口(FunctionalInterface)

news2024/11/6 9:29:12

Java Lambda表达式

  • 定义
    • 背景
    • 示例
      • 匿名类实现
      • Lambda表达式实现
      • 对比匿名类和Lambda实现
      • ==Lambda表达式(调用)说明==
  • Lambda表达式的语法
  • Java 1.8 新特性:函数式接口
    • jdk 1.8 自带的函数式接口 (举例)

定义

参考Oracle官网:https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#syntax

其他文章:https://blog.csdn.net/changlina_1989/article/details/111224385

背景

在这里插入图片描述

One issue with anonymous classes is that if the implementation of your anonymous class is very simple, such as an 
interface that contains only one method, then the syntax of anonymous classes may seem unwieldy and unclear. In 
these cases, you're usually trying to pass functionality as an argument to another method, such as what action should 
be taken when someone clicks a button. Lambda expressions enable you to do this, to treat functionality as method 
argument, or code as data.

The previous section, Anonymous Classes, shows you how to implement a base class without giving it a name. 
Although this is often more concise than a named class, for classes with only one method, even an anonymous class 
seems a bit excessive and cumbersome. Lambda expressions let you express instances of single-method classes 
more compactly.

翻译 :

匿名类有个问题:就是如果匿名类的实现很简单,比如匿名类扩展的基类(如:接口)只有一个方法,那么使用匿名类就显得有点笨拙而不清晰了(简单说就是,使用起来不够简便!)。在这种情况下,总是希望可以将功能直接传给另外一个方法`,例如某人点击按钮后应该采取什么行动。而lambda表达式刚好做到这一点,将功能作为方法的参数或者将代码作为数据。

匿名类可以通过实现一个基类而做到不需要名字。尽管匿名类的实现方式比较简洁但是在类只有一个方法时,匿名类还是有点不够简洁(因为Lambda表达式可以提供一种更简洁的方式)

综上:使用匿名类会比较简洁,使用Lambda表达式更简洁!!

示例

准备一个接口,并声明一个方法:用于匿名类及Lambda表达式

package com.xl.lambda;

public interface PersonInterface {

	String thePerson(Person p);
	
}

对应的实体Person

package com.xl.lambda;

public class Person {
	
	private String name;
	
	private Integer age;
	
	private String sex;
	
	
	public String getName() {
		return name;
	}

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

	public Integer getAge() {
		return age;
	}

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

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}
}

编写测试时类

package com.xl.lambda;

public class LambdaForTest {

	public static void main(String[] args) {
		LambdaForTest lambdaForTest	= new LambdaForTest();
		//lambda表达式调用
		String lambdaResult = lambdaForTest.lambda("John Snow",26,pn -> pn.getAge() > 40 ? "中老年":"青少年");
		System.out.println("Lambda表达式实现:"+lambdaResult);
		
		//匿名类调用
		String anonymousResult = lambdaForTest.anonymous("John Snow",41);
		System.out.println("匿名类实现:"+anonymousResult);
	}
	
	/**
	 * 	使用lambda表达式实现
	 * @param name
	 * @param age
	 * @param p
	 * @return
	 */
	private String lambda(String name, Integer age, PersonInterface p) {
		Person pson = new Person();
		pson .setName(name);
		pson .setAge(age);
		return p.thePerson(pson );
	}
	
	/**
	 * 	使用匿名类实现
	 * @param name 
	 * @param age
	 * @param p
	 * @return
	 */
	private String  anonymous(String name, Integer age) {
		Person pson = new Person();
		pson .setName(name);
		pson .setAge(age);
		return new PersonInterface() {
			@Override
			public String thePerson(Person p) {
				if (p.getAge() > 40) 
					return "中老年";
				return "青少年";
			}
		}.thePerson(pn);
	}

}

匿名类实现

匿名类可参见:https://blog.csdn.net/qq_29025955/article/details/129023869

方法实现
在这里插入图片描述
调用方法:
在这里插入图片描述
运行结果:
在这里插入图片描述

Lambda表达式实现

方法实现:
在这里插入图片描述
调用方法:
在这里插入图片描述
运行结果:
在这里插入图片描述

对比匿名类和Lambda实现

  • 匿名类的方法实现需要11 行代码;而用Lambda表达式只需要4行代码,更加简洁!
  • 但是,匿名类的方法实现可以由任意多个语句块或表达式组成,而Lambda表达式只能将实现(功能)放到一句表达式或一个语句块中。
  • 匿名类省略了类的声明(包括类名字),变得简洁了; Lambda表达式不但省略了类的声明,连方法的声明(包括方法名)都省略了,直接将方法的实现(方法体)当作参数传递给另外一个方法!更简洁。

Lambda表达式(调用)说明

1、lambda表达式将功能(方法体)作为另外一个方法的参数或者将代码作为数据
支撑点:上面《背景》中有说明

2、lambda表达式只能是一句表达式或一个语句块
支撑点:
在这里插入图片描述
3、lambda表达式对应的接口只能有一个抽象方法(没有实现 / 方法体),非default方法和静态方法
支撑点:

  • JDK8开始,接口中方法可以有实现,前面加上default关键字即可,静态方法也可有实现,在接口PersonInterface 中添加如下方法,
package com.xl.lambda;

public interface PersonInterface {

	String thePerson(Person p);
	
	default void another() {
		System.out.println("测试接口的default方法!");
	};
	
	static String third() {
		return "接口的静态方法!";
	}
	
}

LambdaForTest测试类照样可以正常编译、运行!说明Lambda表达式不受影响。
在这里插入图片描述

  • 但是,再加上一个抽象方法,Lambda编译就会报错:
    在这里插入图片描述
    根据错误提示
    The target type of this expression must be a functional interface
    表达式的类型必须是函数式接口。

什么是函数式的接口? : 只有一个抽象方法等待实现的接口。

本例中因为有两个抽象方法 thePerson(Person p) 和 theMan(),所以PersonInterface不是函数式接口。

4、Lambda表达式的使用:

  • 编写方法时,声明一个函数式接口的参数,方法体调用唯一的抽象方法;
  • 调用方法时,直接写上抽象方法的实现/方法体。方法体的返回类型要与抽象方法的返回类型一致!!!
    编写方法:
    在这里插入图片描述
    调用方法:
    在这里插入图片描述
  • 也可以将Lambda表达式单独定义出来
PersonInterface lambda = pn -> pn.getAge() > 40 ? "中老年":"青少年";
//lambda表达式调用2
		PersonInterface lambda = pn -> pn.getAge() > 40 ? "中老年":"青少年";
		String lambdaResult2 = lambdaForTest.lambda("John Snow",26,lambda);
		System.out.println("Lambda表达式实现2:"+lambdaResult2);

在这里插入图片描述

Lambda表达式的语法

参考 oracle:https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#syntax

lambda表达式由以下3部分组成:

  1. A comma-separated list of formal parameters enclosed in parentheses.
    以逗号分隔的参数集合,放在圆括号里面。
  • 没有参数时,直接写对圆即可。
    在这里插入图片描述
  • 只有一个参数时,圆括号可写可不写;
    在这里插入图片描述
  • 两个及以上参数时,需要圆括号;
    在这里插入图片描述
  1. The arrow token, ->
  2. A body, which consists of a single expression or a statement block.
    主体:由单个表达式或者单个语句块组成。

Java 1.8 新特性:函数式接口

参考1: https://zhuanlan.zhihu.com/p/531651771

参考2:Oracle 官网 java se 8 语言规范

什么是函数式接口? 官方定义:
A functional interface is an interface that has just one abstract method (aside from the methods of Object), and thus represents a single function contract.
只有一个抽象方法的接口!代表了单个方法的契约。

jdk 1.8 自带的函数式接口 (举例)

这里以Function<T, R>为例
在这里插入图片描述

/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package java.util.function;

import java.util.Objects;

/**
 * Represents a function that accepts one argument and produces a result.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object)}.
 *
 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

    /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     * Returns a function that always returns its input argument.
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

看完上面代码发现:

  • 共有4个方法
  • 2个default方法:带方法实现的(JDK1.8 新特性,接口的显示声明default的方法可以写实现)
  • 1个静态方法:带方法实现的(JDK1.8 新特性,接口静态方法可以写实现)
  • 1个抽象方法。
    符合函数式接口的定义!但是,在接口声明的上方有个注解:@FunctionalInterface

关于@FunctionalInterface注解

参考 :Oracle 官网 java se 8 语言规范

在这里插入图片描述
The annotation type FunctionalInterface is used to indicate that an interface is meant to be a functional interface (§9.8). It facilitates early detection of inappropriate method declarations appearing in or inherited by an interface that is meant to be functional.
注解FunctionalInterface 用于表明当前接口是函数式接口,它有利于及早的检测当前接口是否满足函数式接口定义(只有一个抽象方法)。

It is a compile-time error if an interface declaration is annotated with @FunctionalInterface but is not, in fact, a functional interface.
如果一个接口加上了注解@FunctionalInterface,但是这个接口有不符合函数式接口的定义,那么就会报编译错误。

package com.xl.lambda;

@FunctionalInterface
public interface TestInterface  {
	
	String test();
//	String test1();
	
	default String defaultImpl() {
		return "jdk1.8新特性:接口的default方法实现";
	}
	static String staticImpl() {
		return "jdk1.8新特性:接口的static方法实现";
	}
	
}

在这里插入图片描述
如果,接口不符函数接口定义并且加上了@FunctionalInterface:如,有两个抽象方法呢?看下面的例子:
在这里插入图片描述
Because some interfaces are functional incidentally, it is not necessary or desirable that all declarations of functional interfaces be annotated with @FunctionalInterface.
如果一些接口碰巧是函数式接口,那么不用必须加@FunctionalInterface注解。

一句话总结:一个接口加了@FunctionalInterface就必须保证这个接口符合函数接口的定义,否则,会报编译错误。另外,函数式接口也可以不用加@FunctionalInterface注解,只要满足函数式接口的定义即可。

加@FunctionalInterface的好处:
如果你想写一个函数式接口,加上@FunctionalInterface注解后,在你写写错时:比如,写了两个及以上的抽象方法或一个抽象方法都没有, 会及时提示你(报编译错误!)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/348308.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

目标检测6--R-FCN中的Position-Sensitive RoI Pooling

文章目录1.介绍2.Position-Sensitive Score Map 和 Position-Sensitive RoI Pooling3.源码参考资料欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; 1.介绍 论文: Region-based Fully Convolutional Networks 代码: R-FCN 本论文作者同9.De…

电子组装流水线MES系统实行条码质量追溯

在电子制造行业&#xff0c;保证生产过程的稳定性与对制造关键能力的改善与提升&#xff0c;是大多数制造企业的管理重心&#xff0c;而缺乏有效的方法与手段。MES系统即制造执行系统&#xff0c;是企业信息集成的纽带&#xff0c;企业实施敏捷制造战略&#xff0c;实现车间生产…

C++学习记录——십 STL初级认识、标准库string类

文章目录1、什么是STL2、STL简介3、什么是string类4、string类的常用接口说明1、常见构造函数2、容量操作3、迭代器4、其他的标准库的string类关于string类的内容&#xff0c;可以在cplusplus.com查看到。 1、什么是STL STL是C标准库的重要组成部分&#xff0c;不仅是一个可复…

指 针

1.指针指针的作用: 可以通过指针间接访问内存&#xff08;可以通过指针的保存一个地址&#xff08;指针--地址&#xff09;&#xff09;内存编号是从0开始记录的&#xff0c;一般用十六进制数字表示。可以利用指针变量保存地址指针变量的定义和使用指针变是定义语法: 数据类型 …

【MFC】模拟采集系统——图形按钮(18)

左边可以简单地使用一个组框&#xff0c;贴上背景图。当然&#xff0c;也可以使用新的对话框。 图形按钮类 1、类向导-》添加类-》选择MFC-》填入新类名称-》选择父类为 CButton 2、添加消息响应函数和虚函数&#xff1a; 消息响应mouse leave (离开&#xff09; mouse move …

CSS3 animation-fill-mode详解

CSS3 animation-fill-mode详解 定义 animation-fill-mode 属性规定当动画不播放时&#xff08;当动画完成时&#xff0c;或当动画有一个延迟未开始播放时&#xff09;&#xff0c;要应用到元素的样式。 默认情况下&#xff0c;CSS 动画在第一个关键帧播放完之前不会影响元素&…

各CCFA类核心期刊的信息汇总与评价总结(科技领域)

CCF中文期刊投稿选择之篇章二:各CCFA类核心期刊的信息汇总与评价总结上一篇章总结一部分期刊的介绍自动化学报相关信息的介绍有关录用比、审稿速度及费用的相关数据收集相关学术论坛上网友的评价与讨论期刊年度出版概况与学术热点动态&#xff08;知网&#xff09;计算机学报相…

2023年可供学习的 10 大 SaaS 知识库工具!

客户迫切希望快速找到所需的信息。在软件行业尤其如此&#xff0c;因为软件行业节奏很快&#xff0c;公司经常销售学习曲线陡峭的产品。为了减缓流失率并提高盈利能力&#xff0c;SaaS 公司正在转向知识库&#xff0c;以帮助他们让客户了解情况。什么是知识库&#xff1f;您可以…

设计模式之代理模式详解和应用

目录1 代理模式定义2 代理模式的应用场景3 代理模式的通用写法4 从静态代理到动态代理5 静态模式在业务中的应用6 动态代理在业务中的应用7 手写JDK动态代理实现原理7.1 JDK动态代理的实现原理7.2 CGLib动态代理容易踩的坑8 CGLib代理调用API及原理分析9 CGLib和JDK动态代理对比…

JVM - 高效并发

目录 Java内存模型和内存间的交互操作 Java内存模型 内存间的交互操作 内存间交互操作的规则 volatile特性 多线程中的可见性 volatile 指令重排原理和规则 指令重排 指令重排的基本规则 多线程中的有序性 线程安全处理 锁优化 锁优化之自旋锁与自适应自旋 锁优…

jvisualvm工具使用

jdk自带的工具jvisualvm&#xff0c;可以分析java内存使用情况&#xff0c;jvm相关的信息。 1、设置jvm启动参数 设置jvm参数**-Xms20m -Xmx20m -XX:PrintGCDetails** 最小和最大堆内存&#xff0c;打印gc详情 2、测试代码 TestScheduleClassGc package com.core.schedule;…

LeetCode 82. 删除排序链表中的重复元素 II

原题链接 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 给定一个已排序的链表的头 headheadhead &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,…

ASML逆袭史:人、资金、技术,缺一不可

前言 近年来&#xff0c;由于众所周知的原因&#xff0c;荷兰ASML&#xff08;阿斯麦&#xff09;公司的先进半导体制造设备——光刻机&#xff0c;进入普通大众视野&#xff0c;成为人们茶余饭后谈论的焦点话题之一。 1月底&#xff0c;“美日荷三方谈判达成协议&#xff0c;可…

Selenium自动化测试Python二:WebDriver基础

欢迎阅读WebDriver基础讲义。本篇讲义将会重点介绍Selenium WebDriver的环境搭建和基本使用方法。 WebDriver环境搭建 Selenium WebDriver 又称为 Selenium2。 Selenium 1 WebDriver Selenium 2 WebDriver是主流Web应用自动化测试框架&#xff0c;具有清晰面向对象 API&…

SAP ABAP 输出结果带有空格

方法一&#xff1a; 字段内容前增加空格&#xff0c;需使用全角空格&#xff0c;使用半角空格时&#xff0c;ALV显示无效&#xff0c;空格无法显示&#xff0c; 全角与半角的切换方法&#xff1a;shift空格切换&#xff0c; 如下的标记部分&#xff0c;要想通过ALV显示空格&…

mfc140u.dll丢失的解决方法,mfc140u.dll文件修复

mfc140u.dll丢失的解决方法&#xff0c;其实要解决这个问题一点都不难&#xff0c;我们主要知道是什么原因造成的&#xff0c;那么就可以轻松的解决。 一.mfc140u.dll是什么 "MFC140u.dll"是一个Windows动态链接库文件&#xff0c;它是Microsoft Visual C 2015运行…

TortoiseSVN的使用

基本概念 版本库 SVN保持数据的地方&#xff0c;所有的文件都保存在这个库中&#xff0c;Tortoise访问的就是远程服务器上的Subversion版本库。 工作拷贝 就是工作副本&#xff0c;可将版本库的文件拷贝到本地中&#xff0c;可以任意修改&#xff0c; 不会影响版本库。在你…

责任链模式(Chain of Responsibility Pattern)

意图&#xff1a;避免请求发送者与接收者耦合在一起&#xff0c;让多个对象都有可能接收请求&#xff0c;将这些对象连接成一条链&#xff0c;并且沿着这条链传递请求&#xff0c;直到有对象处理它为止。 主要解决&#xff1a;职责链上的处理者负责处理请求&#xff0c;客户只…

常用调试golang的bug以及性能问题的实践方法

文章目录如何分析程序运行时间和CPU利用率情况1.shell内置time指令/usr/bin/time指令如何分析golang程序的内存使用情况&#xff1f;1.内存占用情况查看如何分析golang程序的CPU性能情况1.性能分析注意事项2.CPU性能分析A.Web界面查看B.使用pprof工具查看如何分析程序运行时间和…

PHP(12)文件上传

PHP&#xff08;12&#xff09;文件上传一、文件上传原理二、表单写法三、预定义变量 $_FILES四、移动临时文件五、多文件上传1. 同名表单2. 不同名表单六、多文件处理1. 同名文件2. 不同名文件七、封装文件上传函数一、文件上传原理 文件从客户机上传至服务器指定目录。 步骤…