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

定义

参考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注解后,在你写写错时:比如,写了两个及以上的抽象方法或一个抽象方法都没有, 会及时提示你(报编译错误!)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值