好程序员分享java8新特性之Lambda表达式
⼀、 Lambda 表达式简介
什么是 Lambda?
Lambda 表达式是 Java 8 推出的⼀个新特性。从本质上讲, Lambda 表达式是⼀个匿名函数。
为什么要使⽤ Lambda?
使⽤ Lambda 表达式可以对⼀个接⼝进⾏⾮常简洁的实现。
之前我们在给⼀个接⼝引⽤赋值的时候,可以使⽤接⼝实现类,或者匿名内部类。但是有了
Lambda 表达式,我们可以更加⽅便的实现这个需求。
interface Comparator <T > {
int compare (T o1 , T o2 );
}
class Program {
public static void main ( String [] args ) {
// 1. 使⽤接⼝实现类实现
class Impl implements Comparator < Integer > {
@Override
public int compare ( Integer o1 , Integer o2 ) {
return o1 - o2 ;
}
}
Comparator < Integer > c1 = new Impl ();
// 2. 使⽤匿名内部类实现
Comparator < Integer > c2 = new Comparator < Integer > () {
@Override
public int compare ( Integer o1 , Integer o2 ) {
return o1 - o2 ;
}
};
// 3. 使⽤ Lambda 表达式实现
Comparator < Integer > c3 = (o1 , o2 ) -> o1 - o2 ;
}
}
从上述例⼦中,我们可以看到 : 对同样⼀个接⼝引⽤的实现, Lambda 最简单!
Lambda 对接⼝的要求? 虽然 Lambda 表达式可以很便捷的实现接⼝,但并不是所有的接⼝都可以使⽤ Lambda 表达式来实现。
可以⽤ Lambda 表达式来简洁实现的接⼝是有要求的。因为 Lambda 表达式本质上来讲,就是⼀个匿名
函数,⽤这个匿名函数来实现接⼝中的⽅法。所以,如果接⼝中有多个必须要实现抽象⽅法时,
Lambda 表达式将⽆法是⽤。
注: Lambda 表达式要求接⼝中只有⼀个必须要实现的抽象⽅法。但是 JAVA 8 对接⼝也有⼀个
拓展。现在,我们可以在接⼝中,是⽤ default 来修饰⼀个⽅法,此时,这个⽅法需要有实
现。那么,实现类在实现接⼝的时候,对于 default ⽅法,可以实现,也可以不实现。所以,
这⾥所说的接⼝中只有⼀个必须实现的抽象⽅法,与 default ⽅法⽆关。
@FunctionalInterface**
@FunctionalInterface
因为 Lambda 表达式要求接⼝中有且只能有⼀个必须实现的抽象⽅法,所以,对接⼝可以使⽤
@FunctionalInterface 接⼝来进⾏限定。这个注解约束了接⼝中只能有⼀个必须实现的抽象⽅
法。使⽤这个注解修饰的接⼝,⼜叫做函数式接⼝。
⼆、 Lambda 表达式基础语法
Lambda 表达式,本质上就是⼀个匿名⽅法,所以离不开⽅法的⼏个必要的组成部分:返回值、⽅法
名、参数、⽅法体。但是由于他是⼀个匿名⽅法,所以⽅法名可以忽略。同时, Lambda 中也不需要
显式声明返回值类型。所以, 我们在写 Lambda 表达式的时候,只需要关⼼参数和⽅法体即可。
参数: 以 () 包围起来,多个参数以逗号分隔
(int a, int b)
⽅法体: 以 {} 包围起来
{ System.out.println("hello world"); }
-> : Lambda 运算符,⽤来分隔参数和⽅法体
(int a, int b) -> {};
有了这⼏个组成部分,我们就可以对任意的函数式接⼝使⽤ Lambda 进⾏实现
// ⽆参、⽆返回值
() -> { System .out .println ( "hello world" ); };
// int, int 参数、⽆返回值
( int a , int b ) -> { System .out .println (a + b ); };
// int, int 参数、 int 返回值
( int a , int b ) -> { return a + b ; };
三、 Lambda 表达式语法精简
接⼝中定义的⽅法,已经声明了⽅法的参数类型、数量和返回值类型。所以,使⽤ Lambda 表达式在
实现的时候,对应的部分可以省略 参数精简
1. 参数的类型可以精简
(int a, int b) -> { System.out.println(a + b); }
可以精简为:
(a, b) -> { System.out.println(a + b); }
2. 如果参数数量只有⼀个,⼩括号可以精简
(int a) -> { System.out.println(a); }
可以精简为:
a -> { System.out.println(a); }
⽅法体精简
1. 如果⽅法体中只有⼀条语句,则⼩括号可以省略
a -> { System.out.println(a); }
可以精简为:
a -> System.out.println(a);
2. 如果⽅法体中唯⼀的⼀条语句是返回值,在精简掉⼤括号后, return 也必须省略
a -> { return a * 2; }
可以精简为:
a -> a * 2;
四、 Lambda 表达式语法进阶之⽅法引⽤
什么是⽅法引⽤
如果在使⽤ Lambda 进⾏接⼝实现的时候,需要实现的逻辑已经在某⼀个⽅法中实现,则可以直接使
⽤⽅法引⽤,指向指定的⽅法。
interface Calculate {
int calculate ( int a , int b );
}
public class Program {
public static void main ( String [] args ) {
// 接⼝引⽤
Calculate c = (a , b ) -> a + b ;
}
public static int add ( int a , int b ) {
return a + b ;
}
} 在上述代码中, main 函数中 Calculate 引⽤ c 的实现部分,下⾯已经有⼀个⽅法 add 进⾏了实现。
所以此时,我们不需要再实现⼀次,只需要直接指向已经写好的实现即可。所以,可以进⾏如下改
造:
Calculate c = Program ::add ;
上⾯的 Program::add 就是⼀个⽅法引⽤。引⽤了 Program 类中的⼀个静态⽅法 add 。
在使⽤⽅法引⽤的时候需要注意
1. 引⽤的⽅法参数数量、参数类型、返回值类型必须和函数式接⼝中的⽅法定义⼀致。
2. ⽅法引⽤必须有引⽤主体,即⽅法的⾪属者。例如:上⽅的 add ⽅法是⼀个静态⽅法,需要使⽤
类来调⽤。所以⽅法引⽤就是 类 :: ⽅法,如果是⼀个成员⽅法,则需要使⽤ 对象 :: ⽅法 的
形式来引⽤。
构造⽅法的引⽤
如果需要引⽤⼀个构造⽅法,需要使⽤ 类 ::new 进⾏引⽤
interface CreatePerson {
Person getPerson ();
}
class Person {}
class Program {
public static void main ( String [] args ) {
CreatePerson c = Person :: new ;
}
}
五、 Lambda 表达式之综合案例 : 排序 Comparator
// 排序
list .sort ((o1 , o2 ) -> o2 .age - o1 .age );
// 使⽤ Lambda 表达式来实现 Comparator 接⼝,并实例化⼀个 TreeSet 对象
TreeSet <Person > set = new TreeSet <> ((o1 , o2 ) -> {
if (o1 .age >= o2 .age ) {
return - 1 ;
}
else {
return 1 ;
}
});
六、 Lambda 表达式之综合案例 : forEach() // 将集合中的每⼀个元素都带⼊到⽅法 accept 中。
list .forEach (System .out ::println );
// 输出集合中所有的偶数
list .forEach (ele -> {
if (ele % 2 == 0 ) {
System .out .println (ele );
}
});
七、 Lambda 表达式之综合案例 : removeIf()
// 将集合中的每⼀个元素都带⼊到 test ⽅法中 , 如果返回值是 true ,则删除这个元素
// 删除集合中的年龄⼤于 10 岁的元素
list .removeIf (ele -> ele .age > 10 );
⼋、 Lambda 表达式之综合案例 : 线程实例化
new Thread (() -> {
for ( int i = 0 ; i < 100 ; i ++ ) {
System .out .println (i );
}
}).start ();
九、系统内置函数式接⼝
系统已经给我们提供了很多函数式接⼝,⽅便我们的使⽤。因此,如果我们需要⽤到以下⽅法的时
候,不需要再设计接⼝,直接使⽤指定的接⼝即可。 函数式接⼝
参
数
返回值 特殊说明:⼏个特殊实现的⼦接⼝
Predicate T boolean
IntPredicate : 参数: int ,返回值: boolean
LongPredicate : 参数: long ,返回值: boolean
DoublePredicate : 参数: double ,返回值: boolean
Consumer T void
IntConsumer : 参数: int ,返回值: void LongConsumer :
参数: int ,返回值: void DoubleConsumer : 参数: int ,返
回值: void
Function<T, R> T R
IntFunction<R> : 参数: int ,返回值: R
IntToDoubleFunction : 参数: int ,返回值: double
IntToLongFunction : 参数: int ,返回值: long
LongFunction<R> : 参数: long ,返回值: R
LongToDoubleFunction : 参数: long ,返回值: double
LongToIntFunction : 参数: long ,返回值: int
DoubleFunction<R> : 参数: double ,返回值: R
DoubleToIntFunction : 参数: double ,返回值: int
DoubleToLongFunction : 参数: double ,返回值: long
Supplier ⽆ T
BooleanSupplier : 参数:⽆,返回值: boolean
IntSupplier : 参数:⽆,返回值: int LongSupplier : 参
数:⽆,返回值: long DoubleSupplier : 参数:⽆,返回值:
double
UnaryOperator T T
IntUnaryOperator : 参数: int ,返回值: int
LongUnaryOperator : 参数: long ,返回值: long
DoubleUnaryOperator : 参数: double ,返回值: double
BinaryOperator T,
T T
IntBinaryOperator : 参数: int, int ,返回值: int
LongBinaryOperator : 参数: long, long ,返回值: long
DoubleBinaryOperator : 参数: double, double ,返回值:
double
BiPredicate<L,
R>
L,
R boolean
BiConsumer<T,
U>
T,
U void
BiFunction<T,
U, R>
T,
U R
上述接⼝中,最常⽤的是 Predicate 、 Consumer 、
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/69913892/viewspace-2655451/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/69913892/viewspace-2655451/