1、接口的默认方法
2、Lambda 表达式
3、函数式接口
4、方法与构造函数引用
5、Lambda 做用域
6、访问局部变量
7、访问对象字段与静态变量
8、访问接口的默认方法
9、Date API
10、Annotation 注解:支持多重注解java
1、接口的默认方法
Java 8容许咱们给接口添加一个非抽象的方法实现,只须要使用 default关键字便可,这个特征又叫作扩展方法,示例以下:算法
- public interface Formula {
- double calculate(int a);
-
-
- default double sqrt(int a){
- return Math.sqrt(a);
- }
- }
Formula接口在拥有calculate方法以外同时还定义了sqrt方法,实现了Formula接口的子类只须要实现一个calculate方法,默认方法sqrt将在子类上能够直接使用。express
- @Test
- public void test1() {
- Formula formula = new Formula() {
-
- @Override
- public double calculate(int a) {
- return sqrt(a * 100);
- }
- };
-
- System.out.println(formula.calculate(100));
- System.out.println(formula.sqrt(100));
- }
1).若是一个类实现两个接口,这两个接口同时有相同的抽象方法,在类中只须要重写一次这个方法。
2).若是接口中有default修饰的方法不须要重写。
3).若是两个接口里的方法名相同都是default方法,里面的方法体不一样,在类中须要重写该方法。
4).若是两个接口中方法名,参数都相同的方法,一个接口是抽象方法,另外一个是default修饰有方法体。这是该类也必须重写该方法。
5).Lambda表达式中是没法访问到默认方法的api
2、Lambda 表达式
- @Test
- public void test2() {
-
- List<String> list = Arrays.asList("peter","anna","make");
-
-
- Collections.sort(list, new Comparator<String>() {
- @Override
- public int compare(String a, String b) {
- return a.compareTo(b);
- }
- });
-
-
-
- /*
- Collections.sort(list ,(String a,String b) -> {
- return a.compareTo(b);
- });
-
-
- Collections.sort(list, (String a, String b) -> a.compareTo(b));
-
-
- Collections.sort(list, String::compareTo);
-
- System.out.println(Collections.singletonList(list));
- }
1. 什么是λ表达式
λ表达式本质上是一个匿名方法。让咱们来看下面这个例子:
public int add(int x, int y) {
return x + y;
}
转成λ表达式后是这个样子:
(int x, int y) -> x + y;
参数类型也能够省略,Java编译器会根据上下文推断出来:
(x, y) -> x + y; //返回两数之和
或者
(x, y) -> { return x + y; } //显式指明返回值
可见λ表达式有三部分组成:参数列表,箭头(->),以及一个表达式或语句块。
下面这个例子里的λ表达式没有参数,也没有返回值(至关于一个方法接受0个参数,返回void,其实就是Runnable里run方法的一个实现):
() -> { System.out.println("Hello Lambda!"); }
若是只有一个参数且能够被Java推断出类型,那么参数列表的括号也能够省略:
c -> { return c.size(); }jvm
lambda包含3个部分:
(1)括弧包起来的参数
(2)一个箭头
(3)方法体,能够是单个语句,也能够是语句块
参数能够写类型,也能够不写,jvm很智能的,它能本身推算出来
方法能够有返回,也能够无返回,若是有多个语句,还要返回值,须要加上return 。
一个颇有意思的事情:
以前咱们说Object是一切类的父类,然而在加入了lambda之后,这种大一统的局面将不复存在:ide
- @Test
- public void test33() {
- Object r = ()->System.out.println("hello,lambda");
- }
编译报错:incompatible types: Object is not a functional interface
很显然,编译器会检查变量的引用类型里面是否真的是一个函数式接口。那么如何让这段代码经过编译呢?只须要加一个强制类型转换就能够了:函数
- @Test
- public void test33() {
- Object r = (Runnable)()->System.out.println("hello,lambda");
- }
若是你认为lambda表达式仅仅是为了从语法上简化匿名内部类,那就过小看jdk8的lambda了!
lambda的定义:
(1)lambda是方法的实现
(2)lambda是延迟执行的 this
- public static void main(String[] args) {
-
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- System.out.println("hello lambda!");
- }
- };
-
- runnable.run();
-
-
- Runnable runnable1 = () -> {
- System.out.println("hello lambda2!");
- System.out.println("2");
- };
- runnable1.run();
- }
lambda是如何作到的呢?能够反编译后查看字节码。(里面有一个叫作invokedynamic的指令。invokedynamic是从jdk7开始引入的,jdk8开始落地。)能够看出来lambda并非语法糖,它不是像匿名内部类那样生成那种带有$的匿名类。简单的说,这里只是定义了一个方法调用点,具体调用那个方法要到运行时才能决定,这就是前面所说的:延迟执行。具体的细节请google:invokedynamic。
为了配合lambda,jdk8引入了一个新的定义叫作:函数式接口(Functional interfaces)
google
3、函数式接口
(1)是一个接口
(2)只有一个待实现的方法 !!!!
由于jdk8开始,接口能够有default方法,因此,函数式接口也是能够有default方法的,可是,只能有一个未实现的方法。
与此对应,新引入了一个注解: @FunctionalInterface。这个注解只是起文档的做用,说明这个接口是函数式接口,编译器并不会使用这个注解来决定一个接口是否是函数式接口。无论加不加@FunctionalInterface这个注解,下面的接口都是函数式接口:
interface Something {
public String doit(Integer i);
} 人工智能
Lambda表达式是如何在Java的类型系统中表示的呢?每个lambda表达式都对应一个类型,一般是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每个该类型的lambda表达式都会被匹配到这个抽象方法。由于默认方法 不算抽象方法,因此你也能够给你的函数式接口添加默认方法。
咱们能够将lambda表达式看成任意只包含一个抽象方法的接口类型,确保你的接口必定达到这个要求,你只须要给你的接口添加 @FunctionalInterface 注解,编译器若是发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。
- @FunctionalInterface
- public interface Converter<E,T> {
-
- T convert(E str);
-
-
- }
- @Test
- public void test3() {
- Converter<String,Integer> converter = (str) ->Integer.valueOf(str);
-
-
- Integer integer = converter.convert("123");
- System.out.println(integer);
- }
须要注意若是@FunctionalInterface若是没有指定,上面的代码也是对的。
译者注 将lambda表达式映射到一个单方法的接口上,这种作法在Java 8以前就有别的语言实现,好比Rhino JavaScript解释器,
5、Lambda 做用域
在lambda表达式中访问外层做用域和老版本的匿名对象中的方式很类似。你能够直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。
- public class InnerClassExamples {
- public static void main(String... args) {
- Hello h = new Hello();
- h.r.run();
- }
- }
-
- class Hello {
- public Runnable r = new Runnable() {
- public void run() {
-
- System.out.println("-->1 "+this);
- System.out.println("-->2 "+toString());
-
-
- System.out.println("++1 "+Hello.this);
- System.out.println("++2 "+Hello.this.toString());
- }
- };
-
- public String toString() {
- return "Hello's custom toString()";
- }
- }
-->1 com.changwen.jdk8.Hello$1@6d6f6e28 -->2 com.changwen.jdk8.Hello$1@6d6f6e28 ++1 Hello's custom toString() ++2 Hello's custom toString() |
System.out.println(this);这里的this指的是匿名类,而非Hello类。
想要引用Hello类须要Hello.this这样:System.out.println(Hello.this);下面咱们就来看一下伟大的lambda是什么样子的:
- public class Test7{
- public static void main(String args[]){
- Hello2 h = new Hello2();
- h.r.run();
- }
- }
-
- class Hello2{
- public Runnable r = () -> {
- System.out.println(this);
- System.out.println(toString());
- };
-
- public String toString() {
- return "Hello's custom toString()";
- }
- }
Hello's custom toString() Hello's custom toString() |
6、访问局部变量
匿名内部类只能引用做用域外面的final的变量,在lambda中对这个限制作了削弱,只须要是“等价final”就能够,不必用final关键字来标识。
咱们能够直接在lambda表达式中访问外层的局部变量:
- public static void main(String args[]){
- String message = "Howdy, world!";
- Runnable runnable = () -> System.out.println(message);
- runnable.run();
- }
“等效final”的意思是:事实上的final,因此,一旦赋值也是不能够改变的!好比
- public static void main(String args[]){
- String message = "Howdy, world!";
- Runnable runnable = () -> System.out.println(message);
- runnable.run();
-
- message = "change it";
- }
error: local variables referenced from a lambda expression must be final or effectively final
7、方法引用与构造函数引用
真正的大杀器出现了:
如今咱们要把多个Student对象进行排序,有时候是按照firstName来排,有时候是按照lastName或者是age来排,使用lambda能够这样来作:
- public class Student {
- public String firstName;
- public String lastName;
- public int age;
-
- public Student (String firstName, String lastName, int age){
- this.firstName = firstName;
- this.lastName = lastName;
- this.age = age;
- }
-
- public String toString(){
- return firstName+","+lastName+","+age;
- }
- }
- @Test
- public void test12() {
- Student[] students = new Student[]{
- new Student("Ted", "Neward", 41),
- new Student("Charlotte", "Neward", 41),
- new Student("Michael", "Neward", 19),
- new Student("Matthew", "Neward", 13)
- };
-
- Arrays.sort(students, (lhs,rhs)->lhs.firstName.compareTo(rhs.firstName));
-
- System.out.println(Arrays.asList(students));
- }
咱们能够把Comparator抽取出来,变成是Student对象的成员变量:
- public class Student {
- public String firstName;
- public String lastName;
- public int age;
-
- public Student (String firstName, String lastName, int age){
- this.firstName = firstName;
- this.lastName = lastName;
- this.age = age;
- }
-
- public final static Comparator<Student> compareFirstName =
- (lhs, rhs) -> lhs.firstName.compareTo(rhs.firstName);
-
- public final static Comparator<Student> compareLastName =
- (lhs, rhs) -> lhs.lastName.compareTo(rhs.lastName);
-
- public final static Comparator<Student> compareAge =
- (lhs, rhs) -> lhs.age - rhs.age;
-
- public String toString(){
- return firstName+","+lastName+","+age;
- }
- }
- @Test
- public void test12() {
- Student[] students = new Student[]{
- new Student("Ted", "Neward", 41),
- new Student("Charlotte", "Neward", 41),
- new Student("Michael", "Neward", 19),
- new Student("Matthew", "Neward", 13)
- };
-
- Arrays.sort(students, (lhs,rhs)->lhs.firstName.compareTo(rhs.firstName));
- Arrays.sort(students, Student.compareFirstName);
- System.out.println(Arrays.asList(students));
- }
能起到一样的做用,可是语法看上去很奇怪,由于以前咱们都是建立一个知足Comparator签名的方法,而后直接调用,而非定义一个变量,
而后引用这个变量!因此,还有这么一种调用方法: -----------------------------------
- public class Student {
- public String firstName;
- public String lastName;
- public int age;
-
- public Student (String firstName, String lastName, int age){
- this.firstName = firstName;
- this.lastName = lastName;
- this.age = age;
- }
-
- public static int compareFirstName(Student lhs, Student rhs){
- return lhs.firstName.compareTo(rhs.firstName);
- }
- public static int compareLastName(Student lhs, Student rhs){
- return lhs.lastName.compareTo(rhs.lastName);
- }
-
- public String toString(){
- return firstName+","+lastName+","+age;
- }
- }
- @Test
- public void test12() {
- Student[] students = new Student[]{
- new Student("Ted", "Neward", 41),
- new Student("Charlotte", "Neward", 41),
- new Student("Michael", "Neward", 19),
- new Student("Matthew", "Neward", 13)
- };
-
- Arrays.sort(students, Student::compareFirstName);
- System.out.println(Arrays.asList(students));
- }
看Student::compareFirstName这种调用方式,
若是是static方法使用:类名::方法名
若是是instance方法:instance::方法名
可是,上面的代码仍是不是很美观,由于Student只是一个数据对象,它不该该的对外提供compareFirstName或者是compareLastName这样的方法,咱们须要的仅仅是根据某个字段排序而已!很幸运的是jdk的api帮咱们作了这件事:
- public class Student {
- public String firstName;
- public String lastName;
- public int age;
-
- public Student (String firstName, String lastName, int age){
- this.firstName = firstName;
- this.lastName = lastName;
- this.age = age;
- }
- public String getFirstName() {
- return firstName;
- }
-
- public String getLastName() {
- return lastName;
- }
-
- public String toString(){
- return firstName+","+lastName+","+age;
- }
- }
- @Test
- public void test12() {
- Student[] students = new Student[]{
- new Student("Ted", "Neward", 41),
- new Student("Charlotte", "Neward", 41),
- new Student("Michael", "Neward", 19),
- new Student("Matthew", "Neward", 13)
- };
-
- Arrays.sort(students, Comparator.comparing(Student::getFirstName));
- System.out.println(Arrays.asList(students));
- }
注意这里的Person::getFirstName,很显然getFirstName()并非static的,这是jdk作了封装的缘故!
假如咱们的排序算法改成:先按照lastName,而后按照age排序呢?
- @Test
- public void test12() {
- Student[] students = new Student[]{
- new Student("Ted", "Neward", 41),
- new Student("Charlotte", "Neward", 41),
- new Student("Michael", "Neward", 19),
- new Student("Matthew", "Neward", 13)
- };
-
-
- Collections.sort(Arrays.asList(students),
- Comparator.comparing(Student::getLastName).thenComparing(Student::getAge));
- System.out.println(Arrays.asList(students));
- }
还有更多的诸如:andThen()这样的方法,能够查api。
构造函数引用
- public class Person {
- String firstName;
- String lastName;
-
- public Person() {}
-
- public Person(String firstName, String lastName) {
- this.firstName = firstName;
- this.lastName = lastName;
- }
- }
接下来咱们指定一个用来建立Person对象的对象工厂接口:
- public interface PersonFactory<P extends Person> {
- P create(String firstName, String lastName);
- }
这里咱们使用构造函数引用来将他们关联起来,而不是实现一个完整的工厂:
- @Test
- public void test4() {
-
-
- PersonFactory<Person> personFactory = Person::new;
- Person person = personFactory.create("Peter", "Parker");
- }
咱们只须要使用 Person::new 来获取Person类构造函数的引用,Java编译器会自动根据PersonFactory.create方法的签名来选择合适的构造函数。
8、访问对象字段与静态变量
和本地变量不一样的是,lambda内部对于实例的字段以及静态变量是便可读又可写。该行为和匿名对象是一致的:
- class Lambda4 {
- static int outerStaticNum;
- int outerNum;
-
- void testScopes() {
- Converter<Integer, String> stringConverter1 = (from) -> {
- outerNum = 23;
- return String.valueOf(from);
- };
-
- Converter<Integer, String> stringConverter2 = (from) -> {
- outerStaticNum = 72;
- return String.valueOf(from);
- };
- }
- }