当前位置: 首页 > news >正文

JAVA系列---函数式接口

函数式接口的定义

  1. 一个函数式接口有且仅有一个抽象方法(SAM,single abstract method)。对于接口来说抽象方法必须重写,默认方法可选重新,静态方法不可重新。
  2. Object 类中的 public abstract method 不会被视为单一的抽象方法。这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法)。因为任何一个函数式接口的实现,默认都继承了Object类,包含了来自java.lang.Object里对这些抽象方法的实现。
  3. 函数式接口可以用@FunctionalInterface 注解进行修饰。但并不是必须用注解标注,一个接口只要满足只有一个抽象方法的条件就可以称为函数式接口。
    如果使用了此注解,再往接口中新增抽象方法,编译器就会报错,编译不通过。换句话说,@FunctionalInterface就是一个承诺,承诺该接口世世代代都只会存在这一个抽象方法。因此,凡是使用了这个注解的接口,开发者可放心大胆的使用lambda来实例化。当然误用@FunctionalInterface带来的后果也是极其惨重的:如果哪天你把这个注解去掉,再加一个抽象方法,则所有使用lambda实例化该接口的客户端代码将全部编译错误。所以当某接口只有一个抽象方法,但没有增加@FunctionalInterface,则代表该接口不承诺未来不增加抽象方法,所以建议不要用lambda来实例化,还是老老实实的用以前的方式比较稳妥。

函数式接口的示例

@FunctionalInterface
public interface HelloWorldService {void sayHello(String msg);default void doSomeWork1() {// Method body}default void doSomeWork2() {// Method body}static void printHello() {System.out.println("Hello");}@Overrideboolean equals(Object obj);}

函数式接口的用处

函数式接口带给我们最大的好处就是:可以使用极简的lambda表达式实例化接口。为什么这么说呢?我们或多或少使用过一些只有一个抽象方法的接口,比如 Runnable, ActionListener, Comparator …。比如,我们要用Comparator实现排序算法,我们的处理方式通常无外乎两种:

  • 规规矩矩的写一个实现Comparator接口的java类去封装排序逻辑。若业务需要多种排序方式,那就得写多个类提供多种实现,而这些实现往往只需使用一次
  • 另外一种聪明些的做法无外乎就是在需要的地方搞个匿名内部类。比如:
class Test { public static void main(String args[]) { List<Person> persons = new ArrayList<Person>();Collections.sort(persons, new Comparator<Person>(){@Overridepublic int compare(Person o1, Person o2) {return Integer.compare(o1.getAge(), o2.getAge());}});} 
} 

匿名内部类实现的代码量没有多到哪里去,结构也还算清晰。不过如今脚本语言横行无阻,无不以其简洁的语法为杀手锏,俘获程序员的欢心。jdk开发者们应该是意识到了这一点,我们从上面Comparator的实现就可以得到验证。该接口在jdk8的实现增加了FunctionalInterface注解,代表Comparator是一个函数式接口,使用者可放心的通过lambda表达式来实例化。那我们来看看使用lambda实例化所需编写的代码:

Comparator<Person> comparator = (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge());

lambda表达式

lambda这个名称是由美国数学家 Alonzo Church选择的,他在 1930 年代发展计算理论时需要一个希腊字母来表示函数抽象的运算符(λ)。所以其实没什么特殊意义,对于名称纠结症的可以放一放 了。

lambda表达式其实是快速创建函数式接口实例的语法糖; -> 就是lambda的语法标志。

lambda是什么

lambda表达式是实现函数式接口的一个快捷方式。下面这段代码是最直观的体现:

Runnable task = () -> System.out.println("i am coming...");

=号的前面是变量的声明,后面是一个lambda表达式。我们直接将lambda表达式赋值为Runnable类型的task变量,就意味着:lambda表达式是实现函数式接口的一个快捷方式。理解这一点是至关重要的。

lambda表达式语法

lambda表达式语法可抽象表示为:

(Type1 param1, Type2 param2, ..., TypeN paramN) -> {statment1;statment2;...return statmentN;
}

以Predicate判断年龄为例

Predicate<Person> predicate = (Person person) -> {Integer age = person.getAge();return age >= 18;
};

但上面的并不是我们常见的代码,那是因为我们常用的是极简风,以下是简化过程:参数类型省略-》参数占位省略-》返回语句省略。

  1. 参数类型省略:因为编译器可以从上下文环境中推断出lambda表达式的参数类型

    (param1, param2, ..., paramN) -> {statment1;statment2;...return statmentN;
    }
    
    Predicate<Person> predicate = (person) -> {Integer age = person.getAge();return age >= 18;
    };
    
  2. 参数占位省略:参数占位省略存在两种情况,当无参时,必须使用小括号来占位;当有一个参数时,省略小括号即可,参数直接占位

    param -> {statment1;statment2;...return statmentN;
    }
    
    Predicate<Person> predicate = person -> {Integer age = person.getAge();return age >= 18;
    };
    
  3. 表达式省略:当表达式只包含一条语句时,可以同时省略大括号和return关键字和语句结尾的分号。但需要注意的是三者必须同时省略或者同时不省略

    param -> statment;
    
    Predicate<Person> predicate = person -> person.getAge() >= 18
    
    Predicate<Person> predicate = person -> {return person.getAge() >= 18;}
    

lambda表达式与匿名类

lambda表达式与匿名类的异同集中体现在三点上:

  • lambda就是为了优化匿名内部类而生,lambda要比匿名类简洁的多得多;
  • lambda仅适用于函数式接口,匿名类不受限;
  • 在匿名类内部,this关键字指向该抽象类实例,而lambda内部this指向闭包实例。如果需要在内部通过this关键字访问实例,必须使用匿名类;

函数式接口的使用

函数式接口为lambda表达式和方法引用(用冒号::来进行方法的调用)提供目标类型。每个功能接口都有一个抽象方法,称为该功能接口的功能方法,lambda表达式的参数和返回类型与之匹配或适配。功能接口可以在多个上下文中提供目标类型,例如赋值上下文,方法调用或强制转换上下文:

// Assignment context
Predicate<String> p = String::isEmpty;// Method invocation context
stream.filter(e -> e.getSize() > 10)...// Cast context
stream.map((ToIntFunction) e -> e.getSize())...

函数式接口可以使用lambda表达式,方法引用或构造函数引用创建功能接口的实例。

函数式接口有哪些

函数式接口可以对现有的函数友好地支持lambda。

JDK1.8之前已有的函数式接口:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

JDK1.8新增加的函数接口:

  • java.util.function包
函数接口方法名形式化描述分类用途说明
SupplierT get();() -> T供给型提供一个结果
Consumervoid accept(T t)(T) -> void消费型消费一个输出
FunctionR apply(T t)(T) -> R映射型类型转换
Predicateboolean test(T t)(T) -> boolean预测型判断输入是否符合预期
OperatorT applyAsT(T t)(T) -> T映射型数据操纵

Predicate

接受一个输入参数,返回一个布尔值结果

/*** Represents a predicate (boolean-valued function) of one argument.* @param <T> the type of the input to the predicate* @since 1.8*/
@FunctionalInterface
public interface Predicate<T> {/*** Evaluates this predicate on the given argument.*/boolean test(T t);/*** Returns a composed predicate that represents a short-circuiting logical* AND of this predicate and another.  When evaluating the composed* predicate, if this predicate is {@code false}, then the {@code other}* predicate is not evaluated.*/default Predicate<T> and(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) && other.test(t);}/*** Returns a predicate that represents the logical negation of this* predicate.*/default Predicate<T> negate() {return (t) -> !test(t);}/*** Returns a composed predicate that represents a short-circuiting logical* OR of this predicate and another.  When evaluating the composed* predicate, if this predicate is {@code true}, then the {@code other}* predicate is not evaluated.*/default Predicate<T> or(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) || other.test(t);}/*** Returns a predicate that tests if two arguments are equal according* to {@link Objects#equals(Object, Object)}.*/static <T> Predicate<T> isEqual(Object targetRef) {return (null == targetRef)? Objects::isNull: object -> targetRef.equals(object);}
}

示例

public static void main(String[] args) {Predicate<Person> predicate1 = (Person person) -> {Integer age = person.getAge();return age >= 18;};Predicate<Person> predicate2 = (Person person) -> {String name = person.getName();return name.startsWith("R");};Person rico = new Person();rico.setName("Rico");rico.setAge(16);System.out.println("Rico名字以R开头且年满18岁 : " + predicate1.and(predicate2).test(rico));System.out.println("Rico名字以R开头或年满18岁 : " +  predicate1.or(predicate2).test(rico));System.out.println("Rico名字不是以R开头 : " +  predicate1.negate().test(rico));
}
/*** outputRico名字以R开头且年满18岁 : falseRico名字以R开头或年满18岁 : trueRico名字不是以R开头 : true
*/

其它接口

接口方法描述
BiPredicateboolean test(T t, U u);接受两个参数的二元谓词;
DoublePredicateboolean test(double value);入参为double的谓词函数;
IntPredicateboolean test(int value);入参为int的谓词函数;
LongPredicateboolean test(long value);入参为long的谓词函数;

迷糊过程

@FunctionalInterface
public interface Predicate<T> {boolean test(T t);default Predicate<T> and(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) && other.test(t);}
}

使用:

Predicate<Integer> p = i -> i > 2;
System.out.println(p.and(i -> i < 10).test(5));

一开始不理解and方法的执行逻辑,and里面都已经调用过test方法了,为什么还能p.and.test, 原来and方法相当于二次函数式编程,and完毕之后其实相当于:

Predicate<Integer> p = i -> i > 2 && i < 10;
System.out.println(p.test(5));

函数式编程也不会改变java的核心:bean.method(param),jvm只能将bean当参数,所以jvm其实还是将Lambda表达式的结果当成对象,由此表象上看似java支持了函数语句当参数。

Function

接受一个输入参数T,返回一个结果R

/*** Represents a function that accepts one argument and produces a result.* @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.*/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.*/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.*/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.*/static <T> Function<T, T> identity() {return t -> t;}
}

方法表达式:

抽象方法:R=Function(T)
compose方法:V=Function(ParamFunction(T));
andThen方法:V=ParamFunction(Function(T));
identity方法:T=Function(T);

示例

public static void main(String[] args) {Function<Integer, Integer> func1 = x -> {return x + 1;};Function<Integer, Integer> func2 = y -> {return y * 7;};int x = 3, y = 5;System.out.println(func1.apply(x));System.out.println(func2.apply(y));System.out.println(func1.compose(func2).apply(x));System.out.println(func1.andThen(func2).apply(x));
}
/*** output4352228
*/

其它接口

接口方法描述
BiFunctionR apply(T t, U u);接受两个参数,返回一个值,代表一个二元函数;
DoubleFunctionR apply(double value);只处理double类型的一元函数;
IntFunctionR apply(int value);只处理int参数的一元函数;
LongFunctionR apply(long value);只处理long参数的一元函数;
ToDoubleFunctiondouble applyAsDouble(T value);返回double的一元函数;
ToDoubleBiFunctiondouble applyAsDouble(T t, U u);返回double的二元函数;
ToIntFunctionint applyAsInt(T value);返回int的一元函数;
ToIntBiFunctionint applyAsInt(T t, U u);返回int的二元函数;
ToLongFunctionlong applyAsLong(T value);返回long的一元函数;
ToLongBiFunctionlong applyAsLong(T t, U u);返回long的二元函数;
DoubleToIntFunctionint applyAsInt(double value);接受double返回int的一元函数;
DoubleToLongFunctionlong applyAsLong(double value);接受double返回long的一元函数;
IntToDoubleFunctiondouble applyAsDouble(int value);接受int返回double的一元函数;
IntToLongFunctionlong applyAsLong(int value);接受int返回long的一元函数;
LongToDoubleFunctiondouble applyAsDouble(long value);接受long返回double的一元函数;
LongToIntFunctionint applyAsInt(long value);接受long返回int的一元函数;

Consumer

接受一个输入参数,没有返回值

/*** Represents an operation that accepts a single input argument and returns no* result. Unlike most other functional interfaces, {@code Consumer} is expected* to operate via side-effects.*  * @param <T> the type of the input to the operation*  * @since 1.8*/
@FunctionalInterface
public interface Consumer<T> {/*** Performs this operation on the given argument.*/void accept(T t);/*** Returns a composed {@code Consumer} that performs, in sequence, this* operation followed by the {@code after} operation. If performing either* operation throws an exception, it is relayed to the caller of the* composed operation.  If performing this operation throws an exception,* the {@code after} operation will not be performed.*/default Consumer<T> andThen(Consumer<? super T> after) {Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };}
}

示例

public static void main(String[] args) {Person rico = new Person();rico.setName("Rico");rico.setAge(16);Consumer<Person> consumer = person -> System.out.println(person);consumer.accept(rico);
}/*** output{"age":16,"name":"Rico"}
*/

其它接口

接口函数描述
BiConsumervoid accept(T t, U u);接受两个参数
DoubleConsumervoid accept(double value);接受一个double参数
IntConsumervoid accept(int value);接受一个int参数
LongConsumervoid accept(long value);接受一个long参数
ObjDoubleConsumervoid accept(T t, double value);接受一个泛型参数一个double参数
ObjIntConsumervoid accept(T t, int value);接受一个泛型参数一个int参数

Supplier

不需要任何参数,返回一个值

/*** Represents a supplier of results.** @param <T> the type of results supplied by this supplier** @since 1.8*/
@FunctionalInterface
public interface Supplier<T> {/*** Gets a result.*/T get();
}

示例

public static void main(String[] args) {Person rico = new Person();rico.setName("Rico");rico.setAge(16);Supplier<Person> supplier = () -> rico;System.out.println("supplier : " + supplier.get());
}
/*** outputsupplier : {"age":16,"name":"Rico"}
*/

其它接口

接口函数描述
BooleanSupplierboolean getAsBoolean();返回boolean
DoubleSupplierdouble getAsDouble();返回double
IntSupplierint getAsInt();返回int
LongSupplierlong getAsLong();返回long

相关文章:

  • 图像的几何变换之平移
  • 【数据挖掘-思考】分类和聚类
  • Java基础面试重点-1
  • 【java计算机专业毕设】月度员工绩效考核管理系统java MySQL springboot vue maven代码源码 送文档
  • Opus从入门到精通(四)Opus解码程序实现
  • 【CT】LeetCode手撕—102. 二叉树的层序遍历
  • 如何查看当前的gruop_id 的kafka 消费情况 这个可以查看到是否存在消费阻塞问题
  • 记录:UA_Client_readValueAttribute 读取失败 C0错误码
  • RabbitMQ延迟消息(通过死信交换机实现)
  • 电子画册制作与传统画册相比,有哪些优势?
  • nc网络收发测试-tcp客户端\TCP服务器\UDP\UDP广播
  • 仿element-ui 实现自己组件库 <3>
  • 前端 JS 经典:Vue 状态仓库持久化
  • 24年河北自考报名流程详细教程汇总
  • Python实战:小说分词统计-数据可视化
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • Android交互
  • Apache Zeppelin在Apache Trafodion上的可视化
  • co模块的前端实现
  • Docker容器管理
  • emacs初体验
  • Hexo+码云+git快速搭建免费的静态Blog
  • java2019面试题北京
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • JavaScript设计模式之工厂模式
  • Java精华积累:初学者都应该搞懂的问题
  • JS笔记四:作用域、变量(函数)提升
  • Linux各目录及每个目录的详细介绍
  • PHP变量
  • python学习笔记 - ThreadLocal
  • Shadow DOM 内部构造及如何构建独立组件
  • SpingCloudBus整合RabbitMQ
  • Transformer-XL: Unleashing the Potential of Attention Models
  • Twitter赢在开放,三年创造奇迹
  • unity如何实现一个固定宽度的orthagraphic相机
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 工程优化暨babel升级小记
  • 前端技术周刊 2019-01-14:客户端存储
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 微信小程序:实现悬浮返回和分享按钮
  • 消息队列系列二(IOT中消息队列的应用)
  • 异步
  • 最简单的无缝轮播
  • 大数据全解:定义、价值及挑战
  • (C++哈希表01)
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (算法)N皇后问题
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .describe() python_Python-Win32com-Excel