Java 8新特性之Java语言新特性
Lambda表达式和函数式接口 Lambda表达式(也称为闭包),它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理。Java8以前,java开发者只能使用匿名类来代替Lambda表达式。
官方文档
匿名类和Lambda表达式 相比于匿名类,Lambda表达式更加的简洁。
代码如下:
Person类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Person { public enum Sex { MALE, FMALE } String name; LocalDate birthday; Sex gender; String emailAddress; public Person (String name) { this .name=name; this .gender=Sex.MALE; this .emailAddress="emailAddress" ; this .birthday=LocalDate.now(); } public int getAge () { return 0 ; } public void printPerson () { System.out.println("yes" ); } }
LambdaExpressions类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class LambdaExpressions { public static void printPersons (List<Person> roster, CheckPerson tester) { for (Person p : roster) { if (tester.test(p)) { p.printPerson(); } } } public static void main (String[] args) { LinkedList<Person> roster = new LinkedList<>(); Person person=new Person("max" ); roster.add(person); printPersons(roster, new CheckPerson() { @Override public boolean test (Person p) { return p.gender == Person.Sex.MALE && p.getAge() >= 0 && p.getAge() <= 25 ; } }); printPersons(roster, p -> p.gender == Person.Sex.MALE && p.getAge() >= 0 && p.getAge() <= 25 ); } } @FunctionalInterface interface CheckPerson { boolean test (Person p) ; }
Lambda表达式语法 组成 Lambda表达式可由逗号分隔的参数列表、->符号和主体(单个语句或语句块)组成,例如:
1 2 3 p -> p.gender == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
在上面的代码中的参数p的类型由编译器推理得出的,你也可以显式指定该参数的类型,例如:
1 2 3 (Person p) -> p.gender == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
如果Lambda表达式需要更复杂的语句块,则可以使用花括号将该语句块括起来,类似于Java中的函数体,例如:
1 2 3 4 Arrays.asList("a" ,"b" ,"c" ).forEach(e->{ System.out.println(e); System.out.println(e); });
Lambda表达式有返回值,返回值的类型也有编译器推理得出。如果Lambda表达式中的语句只有一行,这可以不用使用return语句,return语句不是表达式; 在lambda表达式中,您必须用大括号({})括起来 ,下面两个代码片段效果相同:
1 Arrays.asList("a" ,"b" ,"c" ).sort((e1,e2)->e1.compareTo(e2));
和
1 2 3 4 5 6 7 8 9 10 11 Arrays.asList("a" ,"b" ,"c" ).sort((e1,e2)->{ return e1.compareTo(e2); }); ``` Lambda表达式可以应用类成员和局部变量(会将这些变量隐式的转换成final ),在上面的匿名类和Lambda表达式中也用到了类成员。 ```java String separator = "," ; Arrays.asList( "a" , "b" , "d" ).forEach( ( String e ) -> System.out.print( e + separator ) );
Lambda的设计者们为了更好的兼容现有的功能,提出了函数式接口。
函数接口是指只有一个函数的接口,这样的接口可以隐式的转换为Lambda表达式。java.lang.Runnable 和java.util.concurrent.Callable 是函数式接口的最佳例子。
在实践中函数接口非常的脆弱,只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口进而导致编译失败。为了克服这种代码层面的脆弱性,并显式说明某个接口是函数式接口,Java 8 提供了一个特殊的注解@FunctionalInterface(Java 库中的所有相关接口都已经带有这个注解了),举个简单的函数式接口的定义(上面也用到了函数接口):
1 2 3 4 @FunctionalInterface interface CheckPerson { boolean test (Person p) ; }
注意: 默认方法 和静态方法 是不会破坏函数接口的定义,下面代码是合法的:
1 2 3 4 5 6 7 @FunctionalInterface public interface FunctionalDefaultMethods { void method () ; default void defaultMethod () { } }
接口的默认方法和静态方法 官方文档
Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法。
默认方法 :默认方法使得开发者可以在 不破坏二进制兼容性的前提下,往现存接口中添加新的方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。
默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写,例子代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private interface Defaulable { default String notRequired () { return "Default implementation" ; } } private static class DefaultableImpl implements Defaulable {} private static class OverridableImpl implements Defaulable { @Override public String notRequired () { return "Overridden implementation" ; } }
Defaulable接口使用关键字default定义了一个默认方法notRequired()。 DefaultableImpl类实现了这个接口,同时默认继承了这个接口中的默认方法。 OverridableImpl类也实现了这个接口,但覆写了该接口的默认方法,并提供了一个不同的实现。
静态方法 :接口可以定义静态方法。 代码如下:
1 2 3 4 5 6 private interface DefaulableFactory { static Defaulable create ( Supplier< Defaulable > supplier ) { return supplier.get(); } }
下面代码片段整合了默认方法和静态方法的使用场景:
1 2 3 4 5 6 7 public static void main ( String[] args ) { Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new ); System.out.println( defaulable.notRequired() ); defaulable = DefaulableFactory.create( OverridableImpl::new ); System.out.println( defaulable.notRequired() ); }
这段代码的输出结果如下:
1 2 Default implementation Overridden implementation
默认方法允许在不打破现有继承体系的基础上改进接口。该特性在官方库中的应用是:给java.util.Collection接口添加新的方法,如stream(),parallelStream(),forEach(),removeIf()等等。
方法引用 官方文档