写在前面的话
总感觉上篇博客有些东西需要补充,于是思来想去写下了本篇博客…
1.场景引入
场景: 假如有这样一种场景,我们的项目里有好多方法,这些方法的参数都包含一个接口,这些接口虽然其功能各不相同,但是却都有一个共同点,就是它们输入参数的个数和类型相同,返回结果的类型也相同.那具体开发中该如何去处理这种情况呢???
解决方式1: 很容易想到的一种解决方式就是我们先去定义这样一个个的接口,再定义一个个的类去实现这些接口,然后将这些实现了指定接口的类作为参数传递给方法(多态),再去解决具体的问题.--------或者定义完接口后,不再去定义具体的类,而直接使用匿名内部类的方式,将所需要的接口参数传递给方法.这两种解决办法都可以算作比较传统的方法,而且可以做到见名之义,但是有一个比较大的缺点,就是需要写的代码太多,比较复杂. 栗子如下:
package com.nrsc.lambda.FunctionInterfaceDemo;
//两个数相加的接口
interface TwoNumberPlus {
int plus(int x, int y);
}
//两个数相减的接口
interface TwoNumberMinus {
int minus(int x, int y);
}
class TwoNumberCalculateClass {
private int x;
private int y;
public TwoNumberCalculateClass(int x, int y) {
this.x = x;
this.y = y;
}
//两个数相加
public int twoNumberPlus(TwoNumberPlus twoNumberPlus) {
return twoNumberPlus.plus(this.x, this.y);
}
//两个数相减
public int twoNumberMinus(TwoNumberMinus twoNumberMinus) {
return twoNumberMinus.minus(this.x, this.y);
}
}
public class Demo1 {
public static void main(String[] args) {
TwoNumberCalculateClass twoNumberCalculateClass = new TwoNumberCalculateClass(7, 8);
//两数相加
int res_plus = twoNumberCalculateClass.twoNumberPlus(new TwoNumberPlus() {
@Override
public int plus(int x, int y) {
return x + y;
}
});
System.out.println(res_plus); //15
//两数相减
int res_minus = twoNumberCalculateClass.twoNumberMinus(new TwoNumberMinus() {
@Override
public int minus(int x, int y) {
return x - y;
}
});
System.out.println(res_minus); //-1
}
}
解决方式2.1: 当然还有一个看起来比较取巧的方法,那就是我们定义一个比较通用的接口,然后使用匿名内部类的方式,去完成我们的任务.栗子如下:
package com.nrsc.lambda.FunctionInterfaceDemo.demo1;
//两个数之间进行计算的通用接口
interface TwoNumberCalculateInterface {
int calculate(int x, int y);
}
class TwoNumberCalculateClass {
private int x;
private int y;
public TwoNumberCalculateClass(int x, int y) {
this.x = x;
this.y = y;
}
//两个数进行计算
public int twoNumberCalculate(TwoNumberCalculateInterface twoNumberCalculate) {
return twoNumberCalculate.calculate(this.x, this.y);
}
}
public class Demo1 {
public static void main(String[] args) {
TwoNumberCalculateClass twoNumberCalculateClass = new TwoNumberCalculateClass(7, 8);
//两数相加
int res_plus = twoNumberCalculateClass.twoNumberCalculate(new TwoNumberCalculateInterface() {
@Override
public int calculate(int x, int y) {
return x + y;
}
});
System.out.println(res_plus); //15
//两数相减
int res_minus = twoNumberCalculateClass.twoNumberCalculate(new TwoNumberCalculateInterface() {
@Override
public int calculate(int x, int y) {
return x - y;
}
});
System.out.println(res_minus); //-1
}
}
从上面的两个栗子来看,方式2.1中的通用接口方式是可行的,但是在jdk8出来之前,应该很少有人会去这么去做,为什么呢?我想最主要的原因应该是,第二种方式其实并没有比第一中方式省下来多少行代码(主要的代码量都在方法的重写上),而且这样还会让代码做不到见名之义的效果.
比较有意思的一个地方
JDK8出来以后,我们发现2.1中所提到的这种通用接口却直接成了JDK源码,而且还一下整出了40个(java.util.function包内的函数式接口). 我想这肯定与lambda表达式的简洁,以及它真正关心的只是输入参数和返回结果的特性相关(归纳一句就是可以使匿名内部类的方式更加简洁). 举个栗子:
package com.nrsc.lambda.FunctionInterfaceDemo;
import java.util.function.BinaryOperator;
class TwoNumberCalculateClass {
private int x;
private int y;
public TwoNumberCalculateClass(int x, int y) {
this.x = x;
this.y = y;
}
//使用JDK自带的函数式接口
public int twoNumberCalculate1(BinaryOperator<Integer> binaryOperator) {
return binaryOperator.apply(this.x, this.y);
}
}
public class Demo1 {
public static void main(String[] args) {
TwoNumberCalculateClass twoNumberCalculateClass = new TwoNumberCalculateClass(7, 8);
/**
* 使用JDK自带的函数式接口以及lambda表达式
*/
int res1 = twoNumberCalculateClass.twoNumberCalculate1((x, y) -> x + y);
System.out.println(res1); //15
/**
* 当然也可以使用原来的匿名内部类的方式----使用JDK自带的函数式接口以及匿名内部类
*/
int res2 = twoNumberCalculateClass.twoNumberCalculate1(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
});
System.out.println(res2);
}
}
但是仅仅如此吗?肯定不是!!!使用JDK自带的函数式接口+lambda表达式虽然可以让匿名内部类的方式大为简化,但仍然会让人感觉代码写的不是那么见名之义, 所以 JDK提供的函数式接口的出现肯定还有其他比较重要的作用 -----比如下篇博客将会介绍到的方法引用.
接下来将介绍一下部分JDK提供的函数式接口的简单使用方式,其实这些接口用起来都很简单,大家尝试着多写写应该就可以掌握,我做的几个小栗子代码如下:
package com.nrsc.lambda.FunctionInterfaceDemo;
import java.text.DecimalFormat;
import java.util.function.*;
/**
* 部分JDK提供的函数式接口使用样例
*/
public class Demo2 {
public static void main(String[] args) {
//断言函数接口--- 接受一个参数,返回一个布尔类型的结果
Predicate<Integer> fun1 = i -> i > 5;
System.out.println(fun1.test(4));
//JDK还提供了一些带类型的函数式接口,用这些接口我们就不必再指定泛型了,如下:
IntPredicate fun11 = i -> i==10;
System.out.println(fun11.test(10));
//消费者----接受一个输入参数并且无返回结果
Consumer fun2 = s -> System.out.println(s + " world");
fun2.accept("hello");
//提供者---无需输入参数, 为我们提供或返回一个参数
Supplier<String> fun3 = () -> "hi , beijing";
System.out.println(fun3.get());
//Function<T,R>---输入参数类型为T,返回类型为R的函数
Function<Integer, String> fun4 = i -> new DecimalFormat("#,###").format(i);
//一元函数-----接受一个参数为类型T,返回值类型也为T。
UnaryOperator<String> fun5 = s -> s.replace(",", "||");
System.out.println(fun5.apply("嘿嘿,哈哈"));
//BiFunction<T ,U ,R> 2个输入的函数-----输入类型为T和U,返回类型为R的函数
BiFunction<Integer, String, Boolean> fun6 = (i, s) -> (i + s).equals("111 hello world");
System.out.println(fun6.apply(111, " hello world"));
//BinaryOperator<T> 二元函数----需要输入两个参数,参数类型都为T,返回类型也为T的函数
BinaryOperator<Integer> fun7 = (s, t) -> s * t;
System.out.println(fun7.apply(7, 8));
}
}