jdk8之前,尤其是在写GUI程序的事件监听的时候,各种的匿名内部类,大把大把拖沓的代码,程序毫无美感可言!既然java中一切皆为对象,那么,就类似于某些动态语言一样,函数也可以当成是对象啊!代码块也可以当成是对象啊!随着函数式编程的概念越来越深入人心,java中CODE=OBJECT的这一天终于到来了!如果你认为lambda表达式仅仅是为了从语法上简化匿名内部类,那就太小看jdk8的lambda了!
下面我们就来看下lambda表达式是如何亮瞎你的眼的!
lambda的定义
Funda-men-tally, a lambda expression is just a shorter way of writing an implementation of a method for later execution.
(1)lambda是方法的实现
(2)lambda是延迟执行的
首先看一个用匿名内部类的例子:
-
public class Test1{
-
public static void main(String args[]){
-
Runnable r = new Runnable(){
-
public void run(){
-
System.out.println( "hello,lambda!");
-
}
-
};
-
r.run();
-
}
-
}
-
public class Test2{
-
public static void main(String args[]){
-
Runnable r = ()->System.out.println( "hello,lambda");
-
r.run();
-
}
-
}
这他妈的得省多少代码啊!
有木有很兴奋啊!
下面还有更刺激的!
lambda是如何做到的呢?看一下反编译之后的字节码:
-
public static void main(java.lang.String[]);
-
descriptor: ([Ljava/lang/String;)V
-
flags: ACC_PUBLIC, ACC_STATIC
-
Code:
-
stack= 1, locals= 2, args_size= 1
-
0: invokedynamic # 2, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
-
5: astore_1
-
6: aload_1
-
7: invokeinterface # 3, 1 // InterfaceMethod java/lang/Runnable.run:()V
-
12: return
-
LineNumberTable:
-
line 3: 0
-
line 4: 6
-
line 5: 12
-
}
可以看出来lambda并不是语法糖,它不是像匿名内部类那样生成那种带有$的匿名类。
简单的说,这里只是定义了一个方法调用点,具体调用那个方法要到运行时才能决定,这就是前面所说的:延迟执行。
具体的细节请google:invokedynamic。
为了配合lambda,jdk8引入了一个新的定义叫做:函数式接口(Functional interfaces)
函数式接口:
it is an interface that requires exactly one method to be implemented in order to satisfy the requirements of the interface.
(1)是一个接口
(2)只有一个待实现的方法
因为jdk8开始,接口可以有default方法,所以,函数式接口也是可以有default方法的,但是,只能有一个未实现的方法。
与此对应,新引入了一个注解: @FunctionalInterface
这个注解只是起文档的作用,说明这个接口是函数式接口,编译器并不会使用这个注解来决定一个接口是不是函数式接口。
不管加不加@FunctionalInterface这个注解,下面的接口都是函数式接口:
interface Something {
public String doit(Integer i);
}
lambda的语法
A lambda in Java essentially consists of three parts: a parenthesized set of parameters, an arrow, and then a body,
which can either be a single expression or a block of Java code.
lambda包含3个部分:
(1)括弧包起来的参数
(2)一个箭头
(3)方法体,可以是单个语句,也可以是语句块
参数可以写类型,也可以不写,jvm很智能的,它能自己推算出来
-
public class Test3{
-
public static void main(String... args) {
-
Comparator<String> c = (String lhs, String rhs) -> lhs.compareTo(rhs);
-
int result = c.compare( "Hello", "World");
-
System.out.println(result);
-
}
-
}
-
public class Test4{
-
public static void main(String... args) {
-
Comparator<String> c =(lhs, rhs) ->{
-
System.out.println( "I am comparing " +lhs + " to " + rhs);
-
return lhs.compareTo(rhs);
-
};
-
int result = c.compare( "Hello", "World");
-
System.out.println(result);
-
}
-
}
一个很有意思的事情:
之前我们说Object是一切类的父类,然而在加入了lambda以后,这种大一统的局面将不复存在:
-
public class Test5{
-
public static void main(String args[]){
-
Object r = ()->System.out.println( "hello,lambda");
-
}
-
-
}
Test5.java:3: error: incompatible types: Object is not a functional interface
Object r = ()->System.out.println("hello,lambda");
^
1 error
很显然,编译器会检查变量的引用类型里面是否真的是一个函数式接口。那么如何让这段代码通过编译呢?
只需要加一个强制类型转换就可以了:
扫描二维码关注公众号,回复:
2199640 查看本文章
-
public class Test6{
-
public static void main(String args[]){
-
Object r = (Runnable)()->System.out.println( "hello,lambda");
-
}
-
-
}
我们知道,在匿名内部类中:
-
class Hello {
-
public Runnable r = new Runnable() {
-
public void run() {
-
System.out.println( this);
-
System.out.println(toString());
-
}
-
};
-
-
public String toString() {
-
return "Hello's custom toString()";
-
}
-
}
-
-
public class InnerClassExamples {
-
public static void main(String... args) {
-
Hello h = new Hello();
-
h.r.run();
-
}
-
}
想要引用Hello类需要Hello.this这样:
-
class Hello {
-
public Runnable r = new Runnable() {
-
public void run() {
-
System.out.println(Hello. this);
-
System.out.println(Hello. this.toString());
-
}
-
};
-
}
下面我们就来看一下伟大的lambda是什么样子的:
-
class Hello{
-
public Runnable r = () -> {
-
System.out.println( this);
-
System.out.println(toString());
-
};
-
-
public String toString() {
-
return "Hello's custom toString()";
-
}
-
}
-
public class Test7{
-
public static void main(String args[]){
-
Hello h = new Hello();
-
h.r.run();
-
}
-
}
Hello's custom toString()
Hello's custom toString()
System.out.println(this);这里的this指的是Hello,而非lambda表达式!
但是,如果我们想在lambda表达式中返回lambda本身该怎么做呢?
变量捕获
匿名内部类只能引用作用域外面的final的变量,在lambda中对这个限制做了削弱,只需要是“等价final”就可以,没必要用final关键字来标识。
-
public class Test8{
-
public static void main(String args[]){
-
String message = "Howdy, world!"; //不需要是final的
-
Runnable r = () -> System.out.println(message); //这里也能访问
-
r.run();
-
}
-
}
-
public class Test9{
-
public static void main(String args[]){
-
String message = "Howdy, world!";
-
Runnable r = () -> System.out.println(message);
-
r.run();
-
message = "change it";
-
}
-
}
Runnable r = () -> System.out.println(message);
^
1 error
如果上面的内容看上去平淡无奇的话,真正的大杀器出现了:方法引用
我们有一个这样的类:
-
class Person {
-
public String firstName;
-
public String lastName;
-
public int age;
-
}
-
class Person{
-
public String firstName;
-
public String lastName;
-
public int age;
-
public Person(String firstName, String lastName, int age){
-
this.firstName = firstName;
-
this.lastName = lastName;
-
this.age = age;
-
}
-
public String toString(){
-
return firstName+ ","+lastName+ ","+age;
-
}
-
}
-
public class Test10{
-
public static void main(String args[]){
-
Person people[] = new Person[]{
-
new Person( "Ted", "Neward", 41),
-
new Person( "Charlotte", "Neward", 41),
-
new Person( "Michael", "Neward", 19),
-
new Person( "Matthew", "Neward", 13)
-
};
-
//sort by firstName
-
Arrays.sort(people, (lhs,rhs)->lhs.firstName.compareTo(rhs.firstName));
-
for(Person p : people){
-
System.out.println(p);
-
}
-
}
-
}
-
class Person{
-
public String firstName;
-
public String lastName;
-
public int age;
-
public Person(String firstName, String lastName, int age){
-
this.firstName = firstName;
-
this.lastName = lastName;
-
this.age = age;
-
}
-
public String toString(){
-
return firstName+ ","+lastName+ ","+age;
-
}
-
public final static Comparator<Person> compareFirstName =
-
(lhs, rhs) -> lhs.firstName.compareTo(rhs.firstName);
-
-
public final static Comparator<Person> compareLastName =
-
(lhs, rhs) -> lhs.lastName.compareTo(rhs.lastName);
-
-
public final static Comparator<Person> compareAge =
-
(lhs, rhs) -> lhs.age - rhs.age;
-
}
-
public class Test11{
-
public static void main(String args[]){
-
Person people[] = new Person[]{
-
new Person( "Ted", "Neward", 41),
-
new Person( "Charlotte", "Neward", 41),
-
new Person( "Michael", "Neward", 19),
-
new Person( "Matthew", "Neward", 13)
-
};
-
Arrays.sort(people, Person.compareFirstName); //这里直接引用lambda
-
for(Person p : people){
-
System.out.println(p);
-
}
-
}
-
}
然后引用这个变量!所以,还有这么一种调用方法:
-
class Person{
-
public String firstName;
-
public String lastName;
-
public int age;
-
public Person(String firstName, String lastName, int age){
-
this.firstName = firstName;
-
this.lastName = lastName;
-
this.age = age;
-
}
-
public String toString(){
-
return firstName+ ","+lastName+ ","+age;
-
}
-
public static int compareFirstName(Person lhs, Person rhs){
-
return lhs.firstName.compareTo(rhs.firstName);
-
}
-
public static int compareLastName(Person lhs, Person rhs){
-
return lhs.lastName.compareTo(rhs.lastName);
-
}
-
}
-
public class Test12{
-
public static void main(String args[]){
-
Person people[] = new Person[]{
-
new Person( "Ted", "Neward", 41),
-
new Person( "Charlotte", "Neward", 41),
-
new Person( "Michael", "Neward", 19),
-
new Person( "Matthew", "Neward", 13)
-
};
-
Arrays.sort(people, Person::compareFirstName);
-
for(Person p : people){
-
System.out.println(p);
-
}
-
}
-
}
如果是static方法使用:类名::方法名
如果是instance方法:instance::方法名
但是,上面的代码还是不是很美观,因为Person只是一个数据对象,它不应该的对外提供compareFirstName或者是compareLastName这样的方法,
我们需要的仅仅是根据某个字段排序而已!很幸运的是jdk的api帮我们做了这件事:
-
import java.util.*;
-
class Person{
-
public String firstName;
-
public String lastName;
-
public int age;
-
public Person(String firstName, String lastName, int age){
-
this.firstName = firstName;
-
this.lastName = lastName;
-
this.age = age;
-
}
-
public String getFirstName(){
-
return this.firstName;
-
}
-
public String getLastName(){
-
return this.lastName;
-
}
-
public String toString(){
-
return firstName+ ","+lastName+ ","+age;
-
}
-
}
-
public class Test13{
-
public static void main(String args[]){
-
Person people[] = new Person[]{
-
new Person( "Ted", "Neward", 41),
-
new Person( "Charlotte", "Neward", 41),
-
new Person( "Michael", "Neward", 19),
-
new Person( "Matthew", "Neward", 13)
-
};
-
Arrays.sort(people, Comparator.comparing(Person::getFirstName));
-
for(Person p : people){
-
System.out.println(p);
-
}
-
}
-
}
但是注意这里的Person::getFirstName,很显然getFirstName()并不是static的,这是jdk做了封装的缘故!
这样做就非常完美了!
假如我们的排序算法改为:先按照lastName,然后按照age排序呢?
-
public class Test15{
-
public static void main(String args[]){
-
Person people[] = new Person[]{
-
new Person( "Ted", "Neward", 10),
-
new Person( "Charlotte", "Neward", 41),
-
new Person( "Michael", "Naward", 19),
-
new Person( "Matthew", "Nmward", 13)
-
};
-
Collections.sort(Arrays.asList(people), (lhs, rhs)->{
-
if(lhs.getLastName().equals(rhs.getLastName())){
-
return lhs.getAge()-rhs.getAge();
-
} else{
-
return lhs.getLastName().compareTo(rhs.getLastName());
-
}
-
});
-
for(Person p : people){
-
System.out.println(p);
-
}
-
}
-
}
-
public class Test16{
-
public static void main(String args[]){
-
Person people[] = new Person[]{
-
new Person( "Ted", "Neward", 10),
-
new Person( "Charlotte", "Neward", 41),
-
new Person( "Michael", "Naward", 19),
-
new Person( "Matthew", "Nmward", 13)
-
};
-
Collections.sort(Arrays.asList(people),Comparator.comparing(Person::getLastName).thenComparing(Person::getAge));
-
for(Person p : people){
-
System.out.println(p);
-
}
-
}
-
}
还有更多的诸如:andThen()这样的方法,可以查api。
虚方法扩展
因为接口可以有default方法,所以很多类库都重写了,加入了一些default的方法,比如:
interface Iterator<T> {
boolean hasNext();
T next();
void remove();
void skip(int i) default {
for (; i > 0 && hasNext(); i--) next();
}
}
skip(i)就是一个default方法,这样所有的Iterator的子类都具有了一个叫skip的方法!
但是,大家对default方法的争议还是比较大的,比如:
-
interface I1 {
-
public default void print(){
-
System.out.println( "I1");
-
}
-
public void hello();
-
}
-
interface I2{
-
public default void print(){
-
System.out.println( "I2");
-
}
-
public void world();
-
}
-
class Impl implements I1,I2{
-
public void hello(){
-
}
-
public void world(){
-
}
-
}
Stream:
之前的文章已经有介绍,下面只据一些使用的例子:
过滤age>12的元素:
-
people
-
.stream()
-
.filter(it -> it.getAge() >= 21) ;
-
people.stream()
-
.filter((it) -> it.getAge() >= 21)
-
.forEach((it) ->
-
System.out.println( "Have a beer, " + it.getFirstName()));
-
Predicate<Person> drinkingAge = (it) -> it.getAge() >= 21;
-
Predicate<Person> brown = (it) -> it.getLastName().equals( "Brown");
-
people.stream()
-
.filter(drinkingAge.and(brown))
-
.forEach((it) ->System.out.println( "Have a beer, " + it.getFirstName()));
-
IntStream ages =
-
people.stream()
-
.mapToInt((it) -> it.getAge());
-
//sum:
-
int sum = people.stream()
-
.mapToInt(Person::getAge)
-
.sum();
-
public class Test17{
-
public static void main(String args[]){
-
List<Integer> values = Arrays.asList( 1, 2, 3, 4, 5);
-
int sum = values.stream().reduce( 0, (l,r)->l+r);
-
System.out.println(sum);
-
}
-
}
二者运算的结果作为下一次运算的左操作数,依次循环。
最后看一个好玩的例子:
-
class Person{
-
public String firstName;
-
public String lastName;
-
public int age;
-
public Person(String firstName, String lastName, int age){
-
this.firstName = firstName;
-
this.lastName = lastName;
-
this.age = age;
-
}
-
public String getFirstName(){
-
return this.firstName;
-
}
-
public String getLastName(){
-
return this.lastName;
-
}
-
public int getAge(){
-
return this.age;
-
}
-
public String toString(){
-
return firstName+ ","+lastName+ ","+age;
-
}
-
public String toJson(){
-
return "{"+
-
"firstName:\""+firstName+ "\","+
-
"lastName:\""+lastName+ "\","+
-
"age:"+age +
-
"}";
-
}
-
}
-
public class Test18{
-
public static void main(String args[]){
-
Person people[] = new Person[]{
-
new Person( "Ted", "Neward", 10),
-
new Person( "Charlotte", "Neward", 41),
-
new Person( "Michael", "Naward", 19),
-
new Person( "Matthew", "Nmward", 13)
-
};
-
String json = Arrays.asList(people).stream().map(Person::toJson).reduce( "[",(l,r)->l + (l.equals( "[")? "": ",") + r)+ "]";
-
System.out.println(json);
-
}
-
}
[{firstName:"Ted",lastName:"Neward",age:10},{firstName:"Charlotte",lastName:"Neward",age:41},{firstName:"Michael",lastName:"Naward",age:19},{firstName:"Matthew",lastName:"Nmward",age:13}]
还可以这样:
-
public class Test19{
-
public static void main(String args[]){
-
Person people[] = new Person[]{
-
new Person( "Ted", "Neward", 10),
-
new Person( "Charlotte", "Neward", 41),
-
new Person( "Michael", "Naward", 19),
-
new Person( "Matthew", "Nmward", 13)
-
};
-
String joined = Arrays.asList(people).stream().map(Person::toJson).collect(Collectors.joining( ", "));
-
System.out.println( "[" + joined + "]");
-
}
-
}
-
更进一步:
-
public class Test20{
-
public static void main(String args[]){
-
Person people[] = new Person[]{
-
new Person( "Ted", "Neward", 10),
-
new Person( "Charlotte", "Neward", 41),
-
new Person( "Michael", "Naward", 19),
-
new Person( "Matthew", "Nmward", 13)
-
};
-
String json = Arrays.asList(people).stream().map(Person::toJson).collect(Collectors.joining( ", ", "[", "]"));
-
System.out.println(json);
-
}
-
}
-
如果只能用一个字来形容,那就是perfect!
http://www.oracle.com/technetwork/articles/java/architect-lambdas-part1-2080972.html
http://www.oracle.com/technetwork/articles/java/architect-lambdas-part2-2081439.html