Java 8 中函数接口分析

C/C++中有函数指针,javascript 中函数可以作为变量使用。在java 8之前,java的回调更多的是使用对象的形式完成。java 8 之后,可以使用函数接口来接受lambda表达式,甚至可以接受具体类中的函数,来进行更加简洁高效的开发。java 8在collection,spliterater, stream中都用到了函数接口。

本篇文章先介绍下java 8之前的回调方式。之后分析下函数接口中几个最常用的接口——Predicate, Consumer, Function,以及他们实际的应用

java 8之前的回调主要使用例如,新建一个监听器接口

/**
 * 
 */
package com.smartchemical.redis_sample.api;

/**
 * @author chenshijue
 *
 */
public interface Listener {
	
	void onMessage();
	
}
新建一个监听器,并作为参数传递给发送函数

/**
 * 
 */
package com.smartchemical.redis_sample.serivce;

import com.smartchemical.redis_sample.api.ISampleDefault;
import com.smartchemical.redis_sample.api.Listener;

/**
 * @author chenshijue
 *
 */
public class ListenerSampleImpl implements ISampleDefault {
	
	protected void sendMessage(Listener listener){
		String newMsg = "new message";
		listener.onMessage(newMsg);
	}

	@Override
	public void run() {
		Listener listener = new MsgListener();
		sendMessage(listener);
	}

}

现在来分析函数接口。

函数接口的定义(来自java.lang包FunctionalInterface注解里面的解释):

Conceptually, a functional interface has exactly one abstract method. 
函数接口概念上来说,就是只有一个抽象方法的接口。而这个抽象方法,可以被之后的lambda表达式、方法引用、构造函数引用覆盖。

理论上来说,java会将所有符合要求的接口看做函数接口。但通常我们会加上@FunctionalInterface注解,这是良好的编程习惯。

下面开始分析几个常用函数接口,Predicate, Consumer, Function。

1. Predicate

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    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.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    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.
     *
     * @return 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.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    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)}.
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return 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);
    }
}
Predicate接口只包含一个抽象方法,boolean test(T t),接受一个参数,返回一个boolean值。从这个抽象方法可以看出,Predicate接口主要作用是判断,例如在collection的removeIf的方法中,Predicate用来接收判断的表达式或者方法,来判断对元素的操作。

public void run() {
		List<Order> orderList1 = new ArrayList<Order>();
		
		for (int i = 0; i < 6; i++){
			Order temp = new Order(String.valueOf(i), "man", 100);
			orderList1.add(temp);
		}
		Predicate<Order> filter = o -> o.getAmount() > 99;
		orderList1.removeIf(filter);
	}
上例中,Predicate接受一个lambda表达式,o -> o.getAmount() > 99,判断o的金额是否超过99。之后在list的removeIf中传入Predicate,list中的超过99的金额就会被删除。

and()方法,此方法返回一个Predicate<T>类型,意味着and方法可以像nodejs的then一样进行链式判断。例如:

public void run() {
		List<Order> orderList1 = new ArrayList<Order>();
		for (int i = 0; i < 6; i++){
			Order temp = new Order(String.valueOf(i), "man", 98 + i);
			orderList1.add(temp);
		}
		Predicate<Order> filter = o -> o.getAmount() > 99;
		Predicate<Order> filter1 = o -> o.getCreatedBy().equals("man");
		orderList1.removeIf(filter.and(filter1));
		System.out.println(orderList1.size());				//结果2
	}
negate()方法,此方法很简答,就是将test方法的值取非。

or()方法,接受另一个Predicate方法作为参数,将参数方法和本身的test方法取或。or()方法和and()方法一样,可以进行链式判断。

isEqual()方法,这个方法就很有趣了。我们看看它的实现:

/**
     * Returns a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}.
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return 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);
    }
首先它接受一个对象作为参数,返回一个Predicate类型的方法,如果参数targetRef为null,则直接返回Objects:null方法。

这个方法有趣的地方在,它接受一个目标对象,返回的不是boolean值,而是一个方法(object -> targetRef.equals(object)),此方法接受另一个对象为参数,判断两个对象是否相等。下面有一个例子便于理解。

public void run() {
		List<Order> orderList1 = new ArrayList<Order>();
		for (int i = 0; i < 6; i++){
			Order temp = new Order(String.valueOf(i), "man", 98 + i);
			orderList1.add(temp);
		}
		Predicate<Order> filter1 = Predicate.isEqual(orderList1.get(2));
		Predicate<Order> filter2 = o -> o.getAmount() > 99;
		Predicate<Order> filter3 = o -> o.getCreatedBy().equals("man");
		orderList1.removeIf(filter1.and(filter2).and(filter3));
		System.out.println(orderList1.size());   //结果为5
	}
以上代码,filter1为判断元素是否和orderList1.get(2)元素相等的方法,filter2是判断金额是否超过99的方法,filter3是判断创建人是否是man的方法。三个filter满足的元素只有一个,因此orderList1中只有一个元素被删除。

2. Consumer

package java.util.function;

import java.util.Objects;

/**
 * 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.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #accept(Object)}.
 *
 * @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.
     *
     * @param t the input 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.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

consumer接口用来表示处理元素的方法。和Predicate方法不同,Predicate只是判断元素是否满足某种条件的函数接口,而Consumer是要对某元素进行操作的函数接口。

accept()方法接受一个参数,不返回结果。我们可以对参数进行处理,当然也可以不处理。

andThen()方法为此接口添加链式处理。

举个简单的例子:

public void run() {
		List<Order> orderList1 = new ArrayList<Order>();
		for (int i = 0; i < 6; i++){
			Order temp = new Order(String.valueOf(i), "man", 98 + i);
			orderList1.add(temp);
		}
		Consumer<Order> con = o -> o.setAmount(200);
		orderList1.forEach(con.andThen(o -> o.setCreatedBy("women")));
		for (Order order :orderList1){
			System.out.println(order);
		}
	}
结果:

Order Id:0	Created by:women	Amount:200.0
Order Id:1	Created by:women	Amount:200.0
Order Id:2	Created by:women	Amount:200.0
Order Id:3	Created by:women	Amount:200.0
Order Id:4	Created by:women	Amount:200.0
Order Id:5	Created by:women	Amount:200.0

3. Function

package java.util.function;

import java.util.Objects;

@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;
    }
}
predicate 仅仅是判断,consumer是修改,那么function就可以理解为映射(或者说可以用来做映射),即将T映射为R。

我们还是来看一个函数接口唯一的抽象方法apply(),接受一个T参数,返回R参数。意思就是可以将T参数做相应的处理,之后返回R参数。

Function函数接口典型应用可以在Stream中找到。Stream中的<R> Stream<R> map(Function<? super T, ? extends R> mapper)方法,接受一个Function接口。在之前的博客可以找到,List可以返回Stream对象,可以用map方法做很多事。例如,转换大小写:

List<String> output = wordList.stream().map(String::toUpperCase).collect(Collectors.toList());
例如,计算平方根:

List<Integer> nums = Arrays.asList(1, 2, 3, 4);
List<Integer> squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList());

再看看Function的其他方法:

compose()方法,用于在当前方法前添加一个方法,并合并两个方法,两个方法之间的参数是必须有因果关系的。例如<V, T>到 <T, R>混合之后,变成<V, R>。

andThen()方法,和compose相反,用于在当前方法后添加一个方法,并合并两个方法,两个方法之间的参数是必须有因果关系的。例如<T, R>到 <R, V>混合之后,变成<T, V>。

identity()方法,返回一个始终返回输入参数的方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值