Java 8 新特性

来源:互联网 发布:d3.js demo文档 编辑:程序博客网 时间:2024/06/10 21:39

一、接口的变化

  • 接口可以通过默认方法和静态方法提供方法的代码实现。
  • 默认方法的开头以关键字default修饰,方法体与常规的类方法相同。
  • 默认方法的出现能帮助库的设计者以后向兼容的方式演进API。
  • 默认方法可以用于创建可选方法和行为的多继承。
  • 解决问题的三条规则:
    (1)类或者父类中声明的方法的优先级高于任何默认方法。
    (2)如果无法依据第一条进行判断,那么子接口的优先级更高:函数签名相同时,优先选择 拥有最具体实现的默认方法的接口,即如果B继承了A,那么B就比A更加具体。
    (3) 最后,如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法。否则将不能编译通过。

二、注解的变化

  • 可以定义重复注解
  • 可以为任何目标添加注解

三、Lambda表达式

可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
- 匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称!
- 函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
- 传递——Lambda表达式可以作为参数传递给方法或存储在变量中。

- 简洁——无需像匿名类那样写很多模板代码。

Lambda 表达式的结构:
- 参数列表——这里它采用了Comparator中compare方法的参数,两个Apple。
- 箭头—— 箭头->把参数列表与Lambda主体分隔开。

- Lambda主体——a1.getWeight().compareTo(a2.getWeight()),表达式就是Lambda的返回值。

Lambda表达式的特点:
- 一个 Lambda 表达式可以有零个或多个参数

  • 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同

  • 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)

  • 空圆括号代表参数集为空。例如:() -> 42

  • 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> a*a

  • Lambda 表达式的主体可包含零条或多条语句

  • 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。

  • 匿名函数的返回类型与该主体表达式一致

  • 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。

  • 表达式就是Lambda的返回值,意味着你无需写return,若表达式没有返回则为空

  • 可以显示的通过return关键字返回值,但必须将主体写在{}中,如(Integer i) -> return “Alan” + i;是错误的写法

- Lambda表达式可以用变量接收。

* 在哪里以及如何使用Lambda: *

可以在函数式接口上使用Lambda表达式。


函数式接口:

函数式接口就是只定义了一个抽象方法的接口。像Java中的Runnable,Comparator等等都只定义了一个方法,这种接口就都是函数式接口。

Java8中接口还可以拥有默认方法(即在类没有对方法进行实现时, 其主体为方法提供默认实现的方法)。哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。

继承了其他接口的接口,如果它总共的抽象方法不止一个,也不是函数式接口。

Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体说来,是函数式接口一个具体实现的实例)。你用匿名内部类也可以完成同样的事情,只不过比较笨拙:需要提供一个实现,然后 再直接内联将它实例化。

请注意,任何函数式接口都不允许抛出受检异常(checked exception)。如果你需要Lambda
表达式来抛出异常,有两种办法:定义一个自己的函数式接口,并声明受检异常,或者把Lambda 包在一个try/catch块中。

示例:

//旧方法:new Thread(new Runnable() {    @Override    public void run() {        System.out.println("Hello from thread");    }}).start();//新方法:new Thread(    () -> System.out.println("Hello from thread")).start();

四、方法引用

(一)、如何创建方法引用

方法引用主要有三类。

(1) 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)。

(2) 指向任意类型实例方法的方法引用(例如String的length方法,写作 String::length)。

(3) 指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction 用于存放Transaction类型的对象,它有个实例方法getValue,那么你就可以写expensive- Transaction::getValue)。

类似于String::length的第二种方法引用的思想就是你在引用一个对象的方法,而这个对象本身是Lambda的一个参数。

例如,Lambda 表达式(String s) -> s.toUppeCase()可以写作String::toUpperCase。

但第三种方法引用 指的是,你在Lambda中调用一个已经存在的外部对象中的方法。

例如,Lambda表达式

()->expensiveTransaction.getValue()可以写作expensiveTransaction::getValue。

(二)、构造函数引用

对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用: ClassName::new。它的功能与指向静态方法的引用类似。例如,假设有一个构造函数没有参数。 它适合Supplier的签名() -> Apple。你可以这样做:

Supplier<Apple> c1 = Apple::new;Apple a1 = c1.get();

这就等价于:

Supplier<Apple> c1 = () -> new Apple();Apple a1 = c1.get();
原创粉丝点击