函数式编程是一种思想,也是一种编程理念。知乎上有个答主我觉得回答的很好,不了解的可以看看。什么是函数式编程思维?-知乎
我们更关注于数据的映射。也就是我们数据中,怎么从A到B的这部分逻辑。
如果你想自己定义个新的函数式接口,强烈建议你加上*@FunctionalInterface*注解。可以更好地揭示我们定义这个接口的意思,同时也可以让编译器帮助我们检查接口定义的正确与否。在任何情况下,我们将一个接口只有一个抽象方法的接口都认为是函数是接口。这样的接口实现,才可以被看成是lambda的表达式。可能你会说,不对啊,明明我看到很多函数式接口是包含了多个方法的。这里需要说明一点的是,函数式接口只能有一个抽象方法,但是可以有多个默认实现的方法。
最简单的函数式接口
通常情况下,我们满足数据的映射,那就是输入一个数据,映射输出一个数据。
比如,我们调用Map方法中computeIfAbsent方法来实现,当我们尝试获取key值为John的值是,如果不存在,则我们生成一个的数据。
Map nameMap = new HashMap<>();
Integer value = nameMap.computeIfAbsent('John', s -> s.length());
当然也可以采用冒号的语法糖
函数式接口含有一个compose方法来组合多个函数表达式。
Function intToString = Object::toString;
Function quote = s -> ''' + s + ''';
Function quoteIntToString = quote.compose(intToString);
assertEquals(''5'', quoteIntToString.apply(5));
其中第一个函数表达式是,将对象转换为字符串,第二个则是对字符串加上双引号。
基础数据类型的函数表达式
在JDK的Function包下的有很多基础类型的函数方法接口,但是这些接口都不是可以直接使用的,都需要自己实现。
IntFunction,LongFunction,DoubleFunction:输入是具体的,但是输出是可以指定的。ToIntFunction,ToLongFunction,ToDoubleFunction:输入是可以自定义的,输出是具体的。DoubleToIntFunction,DoubleToLongFunction,IntToDoubleFunction,IntToLongFunction,LongToIntFunction,LongToDoubleFunction:输入和输出都是指定的基础数据类型。
下面让我们根据一个输入Short然后输出Byte数据类型,来说明用法。javutifunction下不含这两个数据类型映射。
我们先定义一个函数式接口,从Short类型映射到Byte类型。
@FunctionalInterface
public interface ShortToByteFunction {
byte applyAsByte(short s);
}
比如,我们实现如下的逻辑,输入一个short类型的数组,然后每个元素都应用我们定义函数式方法实现。
public byte[] transformArray(short[] array, ShortToByteFunction function) {
byte[] transformedArray = new byte[array.length];
for (int i = 0; i < array.length; i++) {
transformedArray[i] = function.applyAsByte(array[i]);
}
return transformedArray;
}
然后每个元素的逻辑,通过lambda来具体实现。比如,每个将每个short类型都乘以2再转换成Byte。
short[] array = {(short) 1, (short) 2, (short) 3};
byte[] transformedArray = transformArray(array, s -> (byte) (s * 2));
byte[] expectedArray = {(byte) 2, (byte) 4, (byte) 6};
assertArrayEquals(expectedArray, transformedArray);
二元输入参数的函数Two-ArityFunctionSpecializations
也就是输入两个不同的参数,输出一个指定数据类型的函数。在JDK中,带有Bi名称的就是类型。比如BiFunction,ToDoubleBiFunction,ToIntBiFunction,andToLongBiFunctio
一个典型的用法,就是Map中的replaceAll方法。
Map salaries = new HashMap<>();
salaries.put('John', 40000);
salaries.put('Freddy', 30000);
salaries.put('Samuel', 50000);
salaries.replaceAll((name, oldValue) ->
name.equals('Freddy') ? oldValue : oldValue + 10000);
其中,将map中每个元素(key,valu都引用lambda中函数表达式来重新应用。
Suppliers供给型接口&Consumers消费型接口
可以理解为一个生产者,通常没有输入,但是能够更具特定规则输出数据。典型的应用就是一个序列生成器。JDK里面有更丰富接口定义,如BooleanSupplier,DoubleSupplier,LongSupplier和IntSupplier.
int[] fibs = {0, 1};
Stream fibonacci = Stream.generate(() -> {
int result = fibs[1];
int fib3 = fibs[0] + fibs[1];
fibs[0] = fibs[1];
fibs[1] = fib3;
return result;
});
与Supplier对应的Consumer,接收一个输入参数,但是不返回任何数据类型。最常用的就是实现foreach中的消费每个迭代元素。
List names = Arrays.asList('John', 'Freddy', 'Samuel');
names.forEach(name -> System.out.println('Hello, ' + name));
Predicates断言型接口
通常理解,该接口会返回True或False的数据类型,常见的就是Stream中的Filter接口实现的逻辑。
该函数式接口主要就是,输入一个数据类型,返回同样的数据类型。
有一个例外,就是BinaryOperator函数式接口是一个归并操作,汇聚和输入的列表元素。典型的入reduce函数。
List values = Arrays.asList(3, 5, 8, 9, 12);
int sum = values.stream()
.reduce(0, (i1, i2) -> i1 + i2);
参考文献
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点