前言:
方法引用和Lambda表达式必须被赋值,同时编译器需要识别类型信息以确保类型正确。但是你怎么知道传递给方法的参数的类型?
案例1:
x->x.toString()我们清楚这里返回类型必须是String,但x是什么类型呢?Lambda表达式包含类型推导(编译器会自动推导出类型信息,避免了程序员显式地声明)。编译器必须能够以某种方式推导出x的类型。
案例2:
(x,y)->x+y现在x和y可以是任何支持+运算符连接的数据类型,可以是两个不同的数值类型或者是1个String加任意一种可自动转换为String的数据类型(这包括了大多数类型)。但是,当Lambda表达式被赋值时,编译器必须确定x和y的确切类型以生成正确的代码。
为了解决这个问题,Java8引入了java.util.function包。它包含一组接口,这些接口是Lambda表达式和方法引用的目标类型。每个接口只包含一个抽象方法,称为函数式方法。在编写接口时,可以使用@FunctionalInterface注解强制执行此“函数式方法”
一、简单应用
@FunctionalInterface
public interface Functional {
String goodbye(String arg);
}
public interface FunctionalNoAnn {
String goodbye(String arg);
}
@FunctionalInterface
public interface NotFunctional {
//@FunctionalInterface的值在NotFunctional的,定义中可见:接口中如果有多个方法则会产生编译时错误消息
//String goodbye(String arg);
String hello(String arg);
}
public class FunctionalAnnotation {
public String goodbye(String arg){
System.out.println("Goodbye," + arg);
return "";
}
public static void main(String[] args){
FunctionalAnnotation fa = new FunctionalAnnotation();
Functional f = fa::goodbye;
FunctionalNoAnn fna = fa::goodbye;
// Functional fac = fa; //Incompatible
Functional fl = a -> "Goodbye," + a ;
FunctionalNoAnn fnal = a->"Goodbye," + a;
System.out.println(f.goodbye("FunctionalAnnotation"));
System.out.println(fna.goodbye("FunctionalAnnotation"));
System.out.println(fl.goodbye("Functional"));
System.out.println(fnal.goodbye("FunctionalNoAnn"));
}
}
执行结果
---------------------------------------------------------------------------
Goodbye,FunctionalAnnotation
Goodbye,FunctionalAnnotation
Goodbye,Functional
Goodbye,FunctionalNoAnn
- @FunctionalInterface注解是可选的;
- Java在main()中把Functional和Func-tionalNoAnn都当作函数式接口
- @FunctionalInterface的值在NotFunctional的定义中可见:接口中如果有多个方法则会产生编译时错误消息。
- 观察一下f和fna两个变量调用的是类中的方法,而并不是自己接口中的方法,但是我们的FunctionalAnnotation类并没有实现两个函数是接口,。Java8新特性:如果将方法引用或Lambda表达式赋值给函数式接口(类型需要匹配),Java会适配你的赋值到目标接口。编译器会自动包装方法引用或Lambda表达式到实现目标接口的类的实例中(大白话就是,引用匹配方法至关注的是参数类表与返回类型,不关注继承实现引)。
- Functional fac = fa; 会编译错误,因为它没有明确地实现Functional接口。但是,Java8允许我们以简便的语法为接口赋值函数。java.util.function包创建一组完整的目标接口,使得我们一般情况下不需再定义自己的接口。
二、使用函数接口时,名称无关紧要
class In1{}
class In2{}
public class MethodConversion {
static void accept(In1 i1,In2 i2){
System.out.println("accept()");
}
static void someOtherName(In1 i1,In2 i2){
System.out.println("someOtherName()");
}
public static void main(String[]args){
BiConsumer<In1,In2> bic;
bic=MethodConversion::accept;
bic.accept(new In1(),new In2());
bic=MethodConversion::someOtherName;
//bic.someOtherName(newIn1(),newIn2());//Nope
bic.accept(new In1(),new In2());
}
}
-----------------------------------------------------------------
accept()
someOtherName()
- 观察输出结果:在使用函数接口时,名称无关紧要——只要参数类型和返回类型相同。Java会将你的方法映射到接口方法。
三、高阶函数
高阶函数(Higher-orderFunction)只是一个消费或产生函数的函数。
public interface FuncSS extends Function<String,String> {
}
public class ProduceFunction {
static FuncSS produce(){
return s->s.toLowerCase();//[2]
}
public static void main(String[] args){
FuncSS f = produce();
System.out.println(f.apply("YELLING"));
}
}
--------------------------------------------------------------
yelling
- 这里,produce()是高阶函数。
四、andThen()使用
class Apple{
@Override
public String toString(){
return"Apple";
}
}
class Banana{
@Override
public String toString(){
return"Banana";
}
}
public class TransformFunction {
static Function<Apple,Banana> transform(Function<Apple,Banana> in){
return in.andThen(o->{
System.out.println(o);
return o;
});
}
public static void main(String[] args){
Function<Apple,Banana> f2 = transform(i->{
System.out.println(i);
return new Banana();
});
Banana banana = f2.apply(new Apple());
}
}
这里使用到了Function接口中名为andThen()的默认方法,该方法专门用于操作函数。顾名思义,在调用in函数之后调用toThen()(还有个compose()方法,它在in函数之前应用新函数)。要附加一个andThen()函数,我们只需将该函数作为参数传递。transform()产生的是一个新函数,它将in的动作与andThen()参数的动作结合起来。
五、函数式接口替代反射
现实开发中我阅读过好多代码满篇if() else if()...... else(),最长的见过else if() 超越50个,这种代码初入职场写写也就ok,但是真的遇到过5年以上的程序员还是这么写,技术好些的会在业务上分析,或是用反射,但是大家都知道反射的性能并不是很好,如果在数据量较少,性能要求不高的情况下可以使用,java8之后我们好的场景下可以用函数式接口替代反射。
public class Function {
private String var1;
private String var2;
private String var3;
private String var4;
private String var5;
private String var6;
private String var7;
private String var8;
private String var9;
private String var10;
public String getVar1() {
return var1;
}
public void setVar1(String var1) {
this.var1 = var1;
}
public String getVar2() {
return var2;
}
public void setVar2(String var2) {
this.var2 = var2;
}
public String getVar3() {
return var3;
}
public void setVar3(String var3) {
this.var3 = var3;
}
public String getVar4() {
return var4;
}
public void setVar4(String var4) {
this.var4 = var4;
}
public String getVar5() {
return var5;
}
public void setVar5(String var5) {
this.var5 = var5;
}
public String getVar6() {
return var6;
}
public void setVar6(String var6) {
this.var6 = var6;
}
public String getVar7() {
return var7;
}
public void setVar7(String var7) {
this.var7 = var7;
}
public String getVar8() {
return var8;
}
public void setVar8(String var8) {
this.var8 = var8;
}
public String getVar9() {
return var9;
}
public void setVar9(String var9) {
this.var9 = var9;
}
public String getVar10() {
return var10;
}
public void setVar10(String var10) {
this.var10 = var10;
}
}
public class ReflectFunction {
static String[] arr = { "fun01", "fun02", "fun03", "fun04", "fun05", "fun06", "fun07", "fun08", "fun09", "fun10" };
static List<BiConsumer<Function, String>> biConsumers = Arrays.asList(Function::setVar1, Function::setVar2, Function::setVar3,
Function::setVar4, Function::setVar5, Function::setVar6, Function::setVar7, Function::setVar8, Function::setVar9, Function::setVar10);
public static void main(String[] args) {
long start = System.currentTimeMillis();
IntStream.range(0, 100000).forEach(i -> testReflect());
long end = System.currentTimeMillis();
System.out.println(end - start);
long start1 = System.currentTimeMillis();
IntStream.range(0, 100000).forEach(i -> testMethodReference());
long end1 = System.currentTimeMillis();
System.out.println(end1 - start1);
}
static void testReflect() {
Function function = new Function();
try {
Class<?> clazz = Class.forName("test.function.Function");
for (int i = 0; i < arr.length - 1; i++) {
Method method = clazz.getMethod("setVar" + (i + 1), String.class);
method.invoke(function, arr[0]);
}
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
static void testMethodReference() {
Function function = new Function();
for (int i = 0; i < arr.length; i++) {
biConsumers.get(i).accept(function, arr[i]);
}
}
}