简单介绍
函数式接口(Functional Interface)是Java 8对一类特殊类型的接口的称呼。 这类接口只定义了唯一的抽象方法的接口,并且使用@FunctionalInterface 进行注解。
在jdk8中,引入了一个新的包 java.util.function, 提了几种分类场景,使java 8 的函数式编程变得更加简便。
示例
要实现一个场景,业务的执行实现和任务链实现分开。
采用函数式编程进行抽象分离。
如下定义了任务链的逻辑。有三个点:
1、采用泛型定义业务数据类型
2、业务的链条规则采用函数式接口定义
3、业务的执行逻辑采用函数式接口定义。
/**
* 任务链 接口
* @author peter
*
* @param <T>
*/
public interface TaskChainExecutorIntf<T> {
static Logger logger = LoggerFactory.getLogger(TaskChainExecutorIntf.class);
/**
* 任务链处理
*
* @param consumer
* @param map
*/
default void handleAsyncForTaskChain(T t, Function<T, String> functionNext, Consumer<T> consumer,
Map<String, T> map) {
logger.debug("..........handleAsyncForTaskChain print all map task: {}", JSON.toJSONString(map));
consumer.accept(t);
String tempNextName = functionNext.apply(t);
T temp = null;
// 链条下一个任务名 不为空 继续执行
while(!StringUtils.isEmpty(tempNextName)) {
temp = map.get(tempNextName);
if(null == temp) {
logger.error("..........handleAsyncForTaskChain next task error! can not find next task, task name: {}", tempNextName);
break;
}
logger.debug("..........handleAsyncForTaskChain next task get success!, task name: {}", tempNextName);
consumer.accept(temp);
// 重新赋值
tempNextName = functionNext.apply(temp);
}
}
/**
* 非任务链处理
*
* @param consumer
* @param map
*/
default void handleAsync(T t, Consumer<T> consumer) {
consumer.accept(t);
}
}
以下是业务的执行和执行链的结合:
/**
* 处理实现入口
*/
public void process() {
List<ETLDO> list = taskLoader.loadETLTask(YamlConfig.get(sqlMainName).toString(), defaultDataSource, 1, 10);
logger.debug("..........list value :{}", JSON.toJSONString(list));
// 任务链 处理
//TaskChainHandler<ETLDO> chainHandler = new TaskChainHandler<ETLDO>(list);
TaskChainHandler<ETLDO> chainHandler = TaskChainHandler.create(list, chainExecutor);
chainHandler.handler(t -> {
return t.getNext();
}, t -> t.getName(), o -> {
Map<String, Object> etldoMap = new HashMap<String, Object>();
etldoMap = JSON.parseObject(JSON.toJSONString(o), Map.class);
SqlDO sqlDO = SqlParseHelper.parse(YamlConfig.get(sqlMainLock).toString(), etldoMap);
if(lockHandler.lockUnlock(sqlDO.getSql(), sqlDO.getObjArr(), dataSourceContext.getDefaultJdbcTemplate())) {
logger.info("..........ETLDO task lock success! name :{}", o.getName());
handle(o, etldoMap);
}else {
logger.info("..........ETLDO task lock failed, name :{}", o.getName());
}
});
可以看出来,只关注业务的逻辑实现和链条规则,至于怎么串联执行的,通过lambda表达式方式,交给任务链抽闲实现来做。
以上用了java.util.Function包下两个函数接口Function和Consumer。源码如下:
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;
}
}
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); };
}
}
函数式接口的参数可以为函数式接口类型
入定义两个函数式接口:
@FunctionalInterface
public interface IFunc {
String doSay(IFunc2 i);
}
@FunctionalInterface
public interface IFunc2 {
String doSay();
}
其中接口IFunc中的方法参数 类型为 接口IFunc2
执行测试代码:
public class Test {
public static void test(IFunc f, IFunc2 f2) {
f.doSay(f2);
}
public static void test2(IFunc f) {
IFunc2 f2 = () -> {System.out.println("this is func2"); return "2";};
f.doSay(f2);
}
public static void test3(IFunc f) {
f.doSay(() -> {System.out.println("this is func2 test3"); return "2";});
}
public static void main(String[] args) {
test((a) -> {
System.out.println("this is func1");
a.doSay();
return "1";}, () -> {System.out.println("this is func2"); return "2";});
test2((a) -> {
System.out.println("this is func1");
a.doSay();
return "1";});
test3((a) -> {
System.out.println("this is func1");
a.doSay();
return "1";});
}
}
this is func1
this is func2
this is func1
this is func2
this is func1
this is func2 test3
需要注意,当中的a只是引用。
java标准function
在java.util.function package下,大致分为四类:
- Function: 接收参数,并返回结果,主要方法 R apply(T t)
UnaryOperation 接收与返回类型相同,主要方法 T apply(T t)
BiFunction 接收两个参数,并返回结果,主要方法 R apply(T t, U u)
BinaryOperation 接收的两个参数与返回类型均相同,主要方法 T apply(T t, T u) - Consumer: 接收参数,无返回结果,主要方法 void accept(T t)
BiConsumer 接收两个参数,无返回结果,主要方法 void accept(T t, U u) - Supplier: 不接收参数,但返回结构,主要方法 T get()
- Predicate: 接收参数,返回boolean值,主要方法 boolean test(T t)
BiPredicate 接收两个参数,返回boolean值,主要方法 boolean test(T t, U u)
如下图:
总结
函数式编程,简洁优美,缩短了抽象与实现的路径。