Java8的新特性之一,就是首次引入了函数式编程Lambda表达式,按oracle的说法,是为了引导java向函数式编程的方向发展。java
在JDK1.8中,多了一个包,java.util.function,这里主要用到了这个包下面的两个接口:编程
Consumer<T> //Represents an operation that accepts a single input argument and returns no result. Function<T,R> //Represents a function that accepts one argument and produces a result.
要解释清楚这个问题,首先得从lambda表达式提及oracle
(x,y)->doSomethingWith(x,y);app
这句话就是一个lambda表达式的例子;"->"是Java8新定义的一个操做符,操做符左边表明lambda表达式接收的参数,这里它接收了两个参数,x和y;表达式右边是函数操做,也就是对这两个变量执行某种操做。如x+y,x*y等。函数式编程
简单的解释了一下java8的lambda表达式,接下来进入正题:函数
在java8中,function包下面的全部接口都描述了一种预约义的lambda表达式类型,换句话说,就是能够经过声名接口类型的变量为lambda赋值,从而达到函数参数化的目的,这样说可能比较抽象,看代码指针
public class LambdaDemo { public static void adapter(Function<String, String> function) { String message = "Hello World"; System.out.println(function.apply(message)); } public static void main(String[] args) { Function<String, String> function1 = (str) -> { return str.toUpperCase(); }; Function<String, String> function2 = (str) -> { return str.toLowerCase(); }; adapter(function1); adapter(function2); } }
仔细体会上面的代码,函数adaper这里表示对字符串进行某种适配并将它打印出来,而具体的适配方式是经过参数传过来的,咱们来看看运行结果:code
HELLO WORLD hello world
和预期的彻底同样,若是你能看懂上面的代码,我相信你已经基本明白了java8的函数式接口用法。orm
下面我针对上面提到的两个接口作一下解释:接口
全部的接口都是泛型定义的,泛型的做用在于类型推断,也就是说你指定了lambda的类型,那么他接收的参数的类型就是肯定的,编译器就能够推断lambda的类型。事实上,在“->”运算符左边括号内的参数都是“匿名”的,你既无需考虑它们的引用,也无需事先声名它们,它们只在当前lambda表达式内做用,而且类型已经肯定。再深刻思考一点,若是你熟悉接口重载,你可能以为这和泛型同样,是一块语法糖,事实上并不是如此,Oracle为了引导java向函数式编程的方向发展,放弃了简单的接口重载,而是经过动态调用(invokeddynamic:https://stackoverflow.com/questions/30002380/why-are-java-8-lambdas-invoked-using-invokedynamic)实现的。
再说说这两个接口的区别:
Consumer 中文译做“消费者”,它经过接口下的accept方法,接收惟一的参数,并执行操做;参数和调用该方法的上下文是无关的,也就是说对变量执行的操做不影响原上下文中的变量;
Function 接口则能够经过调用apply方法返回一个值,从而供以后调用。
要解释清楚这一问题,还得靠代码:
public class ContextDemo { public static String transform(String str) { return str.concat(" World"); } public static void main(String[] args) { Function<String, String> function = (str) -> { return transform(str); }; Consumer<String> consumer = (str) -> { str = transform(str); }; String msg = "Hello"; System.out.println(msg); consumer.accept(msg); System.out.println(msg); msg = function.apply(msg); System.out.println(msg); } }
运行结果:
Hello Hello Hello World
可见,Consumer并无影响到它的上下文,它用的参数是变量的“副本”;而不是变量的指针。
接下来讲说相似js中的callback();
对于一项功能,若是咱们可以提供多个参数,咱们倾向于使用函数或者方法来实现;可是若是咱们须要用到多个参数,因为函数至多只有一个返回值,因此此时采用方法的思想咱们须要多个函数或者方法,这时最简单的就是将方法传过去,而不用返回,相似的场景在JS中很是常见
function doSomething(callback){ var a = 1; var b = 2; callback(a,b); } doSomething(function(a,b){ alert(a + b); });
jQuery Ajax方法中success场景下的data就是一个典型的callback,如今java也能够实现相似的效果,从而提升代码重用率
public class CallbackDemo { public static void main(String[] args) { sayHello((msg) -> { System.out.println(msg); }); sayHello((msg) -> { System.out.println(msg.toUpperCase()); }); sayHello((msg) -> { System.out.println(msg.replaceAll("o", "0")); }); } public static void sayHello(Consumer<String> callback) { String msg = "Hello World"; callback.accept(msg); } }
运行结果以下:
Hello World HELLO WORLD Hell0 W0rld
初来乍到,有错误还望批评指正。