目录
- 1.1为什么要学函数式编程
- 1.2 函数式编程思想
- 2.Lambda表达式
- 2.1 思想及原则
- 2.2 示例
- 2.3 省略规则
1.1为什么要学函数式编程
java8引入了函数式编程,在工作中应用得特别广泛,如果不学习可能会看不懂公司中同事的代码。
函数式编程对于海量数据的处理特别有帮助,提供了并行流,可以让程序员不用自己进行并发编程(这无疑是有难度的)。
代码可读性其实会更高,避免嵌套地狱,可以看看下面这个例子。
1.2 函数式编程思想
面向对象编程关注什么对象做什么事情,但是函数事编程把关注点转移到了数据:对数据做哪些处理。
优点有:
- 代码简洁
- 接近于自然语言,易于理解(看名字就大致知道在做什么操作)
- 易于"并发编程"(使用并行流处理数据,比单线程处理更具有效率,不必自己并发编程,去解决头疼的数据安全问题)
2.Lambda表达式
2.1 思想及原则
jdk8的语法糖,主要是优化部分内部类的操作方法。它是函数式编程的一个重要体现——让我们不用关注是什么类,什么方法,而只需要关注对数据进行什么操作。
核心原则是可推导、可省略
。
2.2 示例
例1:
匿名内部类创建线程。
代码语言:javascript复制 new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我是一只小小鸟...");
}
}).start();
使用Lambda简化下。
代码语言:javascript复制new Thread(() -> System.out.println("想要飞却怎么也飞不高...")).start();;
什么时候可以使用Lambda表达式呢?答案是匿名内部类的参数是一个接口,并且只有一个抽象方法需要被实现(可以有默认方法)。这就是所谓的函数式接口。参考Runnable
接口。@FunctionalInterface
可以用来标注一个接口是函数式接口。
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
我们到这里就可以领会开始说的函数式编程思想了:Lambda表达式不关注类名,不关注方法名,因此他们都可以省略。只关注是什么数据(参数),对数据做什么操作(方法体)。
下面来做一个练习。
例2:调用下面方法实现对a,b进行自定义运算。
代码语言:javascript复制 public static int calculateNum(IntBinaryOperator operator) {
int a = 10;
int b = 20;
return operator.applyAsInt(a, b);
}
参数IntBinaryOperator
java的util包中自带的工具类,我们虽然不认识,但可以点进去看看,是个函数式接口。
@FunctionalInterface
public interface IntBinaryOperator {
/**
* Applies this operator to the given operands.
*
* @param left the first operand
* @param right the second operand
* @return the operator result
*/
int applyAsInt(int left, int right);
}
实现就变得很简单了。
先做一个内部类的调用实现。
代码语言:javascript复制 int cal1 = calculateNum(
new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
return left right;
}
}
);
再写一个Lambda实现。
代码语言:javascript复制 int cal2 = calculateNum((left,right)->{
return left* right;
});
再来体会下Lambda表达式所展示的函数式编程思想:我并不关心calculateNum中是哪个类作为参数,也不关心这个类中是哪个方法来帮我做操作,我只像关心,我要传几个参数,我对我的数据进行什么操作。这样我们就可以把那些无关必要的东西省略,让我们代码变得更加简洁。
而且,写一个Lambda表达式在idea中特别简单,只要一个匿名内部类可以被简化未Lambda表达式,我们都可以先把鼠标光标移动到方法参数上,使用Alt
enter
将它简化成Lambda表达式。而且一个Lambda表达式看不懂,也可以使用同样的方式转换为匿名内部类。
例3:输入过滤条件,打印符合条件的参数。
代码语言:javascript复制 public static void printNum(IntPredicate predicate) {
int[] arr = {1,2,3,4,5,6,7,8,9};
for(int num : arr) {
if(predicate.test(num)) {
System.out.println(num);
}
}
}
IntPredicate
是函数式接口。虽然有两个默认方法,但是只有一个抽象方法需要被实现。
@FunctionalInterface
public interface IntPredicate {
boolean test(int value);
default IntPredicate and(IntPredicate other) {
Objects.requireNonNull(other);
return (value) -> test(value) && other.test(value);
}
default IntPredicate negate() {
return (value) -> !test(value);
}
default IntPredicate or(IntPredicate other) {
Objects.requireNonNull(other);
return (value) -> test(value) || other.test(value);
}
}
实现如下。
代码语言:javascript复制 printNum(new IntPredicate() {
@Override
public boolean test(int num) {
return num > 4;
}
});
printNum((int num)->{
return num < 4;
});
例4:对字符串转成数组。
代码语言:javascript复制public static <R> R typeConver(Function<String, R> function) {
String str = "12345";
return function.apply(str);
}
实现如下。
代码语言:javascript复制 int c = typeConver((String str) -> {
return Integer.parseInt(str);
});
例5:输出数组中的元素。
代码语言:javascript复制 public static void forEachArr(IntConsumer consumer) {
int[] arr = {1,2,3,4,5};
for(int num : arr) {
consumer.accept(num);
}
}
操作如下。
代码语言:javascript复制 forEachArr((int num) -> {
System.out.println(num);
});
上面的例子其实都是很常用的,我们可以熟练应用到项目中。
2.3 省略规则
上面的Lambda表达式甚至可以进一步省略。
(1)参数列表可以省略。
如,forEachArr省略int
:
forEachArr((num) -> {
System.out.println(num);
});
原因是IntConsumer
只有如下一个抽象方法,编译器可以推导出参数类型为int
。
void accept(int value);
(2)方法体只有一个语句时,{}
与;
可以省略
(3)参数只有一个时,()
可以省略。
forEachArr(num -> System.out.println(num));
(4)记不住可以不记,使用Idea工具转换。