jdk8系列一、jdk8 Lamda表达式语法、接口的默认方法和静态方法、supplier用法
一、简介
毫无疑问,Java 8是Java自Java 5(发布于2004年)之后的最重要的版本。这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。
在本文中我们将学习这些新特性,并用实际的例子说明在什么场景下适合使用。
包含Java开发者经常面对的几类问题:
- 语言
- 编译器
- 库
- 工具
- 运行时(JVM)
二、Lambda表达式和函数式接口
Lambda表达式(也称为闭包)是Java 8中最大和最令人期待的语言改变。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理:函数式开发者非常熟悉这些概念。
很多JVM平台上的语言(Groovy、Scala等)从诞生之日就支持Lambda表达式,但是Java开发者没有选择,只能使用匿名内部类代替Lambda表达式。
Lambda的设计耗费了很多时间和很大的社区力量,最终找到一种折中的实现方案,可以实现简洁而紧凑的语言结构。
1、Lamda表达式语法
首先不看Lambda本身的写法,可以发现,对于i值的访问,在Lambda中已经不需要声明i为final了。
其次,要明白一个重要的道理:Lambda要求实现的接口中只有一个方法,像上面的Runnable接口就只有一个run方法,如果一个接口中有多于一个方法,则不能写成Lambda的形式。
最简单的Lambda表达式可由逗号分隔的参数列表、->符号和语句块组成,例如:
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
在上面这个代码中的参数e的类型是由编译器推理得出的,你也可以显式指定该参数的类型,例如:
Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
2、Lamda表达式语句块
如果Lambda表达式需要更复杂的语句块,则可以使用花括号将该语句块括起来,类似于Java中的函数体,例如:
public static void main(String[] args) {
String separator = ",";
Arrays.asList("a","b","c").forEach((String e) -> {
System.out.print(e + separator);
System.out.println(e);
});
}
Lambda表达式可以引用类成员和局部变量(会将这些变量隐式得转换成final的),例如下列两个代码块的效果完全相同:
String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
( String e ) -> System.out.print( e + separator ) );
和
final String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
( String e ) -> System.out.print( e + separator ) );
3、处理返回值
Lambda表达式有返回值,返回值的类型也由编译器推理得出。如果Lambda表达式中的语句块只有一行,则可以不用使用return语句,下列两个代码片段效果相同:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
和
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
int result = e1.compareTo( e2 );
return result;
} );
4、函数接口
Lambda的设计者们为了让现有的功能与Lambda表达式良好兼容,考虑了很多方法,于是产生了函数接口这个概念。函数接口指的是只有一个函数的接口,这样的接口可以隐式转换为Lambda表达式。java.lang.Runnable和java.util.concurrent.Callable是函数式接口的最佳例子。在实践中,函数式接口非常脆弱:只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口进而导致编译失败。为了克服这种代码层面的脆弱性,并显式说明某个接口是函数式接口,Java 8 提供了一个特殊的注解@FunctionalInterface(Java 库中的所有相关接口都已经带有这个注解了),举个简单的函数式接口的定义:
@FunctionalInterface
public interface Functional {
void method();
}
不过有一点需要注意,默认方法和静态方法不会破坏函数式接口的定义,因此如下的代码是合法的。
@FunctionalInterface
public interface FunctionalDefaultMethods {
void method(); default void defaultMethod() {
}
}
更多可以参考官方文档。
三、接口的默认方法和静态方法
1、允许接口有默认实现
Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法。默认方法使得接口有点类似traits,不过要实现的目标不一样。默认方法使得开发者可以在 不破坏二进制兼容性的前提下,往现存接口中添加新的方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。
默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写,例子代码如下:
public interface Animal { default String getName(){
return "animal";
}
}
private static class DefaultableImpl implements Defaulable {
} private static class OverridableImpl implements Defaulable {
@Override
public String notRequired() {
return "Overridden implementation";
}
}
2、接口静态方法
Dog接口使用关键字Animal定义了一个默认方法getName()。Cat类实现了这个接口,同时默认继承了这个接口中的默认方法;Dog类也实现了这个接口,但覆写了该接口的默认方法,并提供了一个不同的实现。
Java 8带来的另一个有趣的特性是在接口中可以定义静态方法,例子代码如下:
private interface AnimalFactory {
// Interfaces now allow static methods
static Animal create( Supplier<Animal> supplier ) {
return supplier.get();
}
}
下面的代码片段整合了默认方法和静态方法的使用场景:
Animal dog = AnimalFactory.create(Dog::new);
Animal cat = AnimalFactory.create(Cat::new);
System.out.println(dog.getName());
System.out.println(cat.getName());
这段代码的输出结果如下:
dog
animal
由于JVM上的默认方法的实现在字节码层面提供了支持,因此效率非常高。默认方法允许在不打破现有继承体系的基础上改进接口。该特性在官方库中的应用是:给java.util.Collection接口添加新方法,如stream()、parallelStream()、forEach()和removeIf()等等。
尽管默认方法有这么多好处,但在实际开发中应该谨慎使用:在复杂的继承体系中,默认方法可能引起歧义和编译错误。如果你想了解更多细节,可以参考官方文档。
四、supplier用法
supplier也是是用来创建对象的,但是不同于传统的创建对象语法:new,看下面代码:
public class TestSupplier {
private int age; TestSupplier(){
System.out.println(age);
}
public static void main(String[] args) {
//创建Supplier容器,声明为TestSupplier类型,此时并不会调用对象的构造方法,即不会创建对象
Supplier<TestSupplier> sup= TestSupplier::new;
System.out.println("--------");
//调用get()方法,此时会调用对象的构造方法,即获得到真正对象
sup.get();
//每次get都会调用构造方法,即获取的对象不同
sup.get();
}
}
jdk8系列一、jdk8 Lamda表达式语法、接口的默认方法和静态方法、supplier用法的更多相关文章
- java8-新特性--(接口的默认方法与静态方法)
Java 8用默认方法与静态方法这两个新概念来扩展接口的声明. public interface Inte{ void method(); default void defaultMethod(){ ...
- java8新特性:接口的默认方法与静态方法
接口中一共可以定义三种方法: 1.抽象方法,也就是需要实现者必须实现的方法,最常见的那种 2.默认方法,不需要实现者实现 3.静态方法,不需要实现者实现 默认方法: 允许在已有的接口中添加新方法,而同 ...
- JDK8新增接口的默认方法与静态方法
JDK8之前,interface中可以定义常量和抽象方法,访问修饰符是public. public interface A { /** a1和a2写法是等价的 */ public static fin ...
- JDK8.0接口中的默认方法和静态方法
我们在接口中通常定义的方法是抽象方法,即没有方法体,只有返回值类型和方法名:(public abstract) void Method(); 类在实现接口的时候必须重写抽象方法才可以 jdk8中新加的 ...
- c# 匿名函数和lamda表达式语法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- JDK8新特性之接口默认方法与静态方法
接口默认方法与静态方法 有这样一些场景,如果一个接口要添加一个方法,那所有的接口实现类都要去实现,而某些实现类根本就不需要实现这个方法也要写一个空实现,所以接口默认方法就是为了解决这个问题. 接口静态 ...
- Java8新特性之四:接口默认方法和静态方法
在JDK1.8以前,接口(interface)没有提供任何具体的实现,在<JAVA编程思想>中是这样描述的:"interface这个关键字产生了一个完全抽象的类,它根本就没有提供 ...
- JAVA_接口_默认方法&静态方法
1.小结(注意): 1.接口中无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰 2.接口中,没有构造方法,不能创建对象 3.接口中,没有静态代码块 ...
- 【Java8新特性】- 接口中默认方法修饰为普通方法
Java8新特性 - 接口中默认方法修饰为普通方法 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学 ...
随机推荐
- [CF1132F]Clear the String
题意 给你一个串s,每次可以花费1的代价删去一个子串,要求子串的每一位为同一个字符. 求删去整个串的最小代价. 分析 这是一道非常简单的区间\(DP\)问题,我们定义\(f[i][j]\)表示删去子串 ...
- 洛谷 P4151 [WC2011]最大XOR和路径 解题报告
P4151 [WC2011]最大XOR和路径 题意 求无向带权图的最大异或路径 范围 思路还是很厉害的,上午想了好一会儿都不知道怎么做 先随便求出一颗生成树,然后每条返祖边都可以出现一个环,从的路径上 ...
- Android 友盟SDK 终极解决报错:SocialSDK_QQZone_2.jar contains native libraries that
转自:http://bbs.umeng.com/thread-6552-1-2.html 报错信息:The library `SocialSDK_QQZone_2.jar` contains nati ...
- SpringMvc的Url映射和传参案例
Springmvc的基本使用,包括url映射.参数映射.页面跳转.ajax和文件上传 以前学习的时候写的代码案例,今天整理笔记的时候找到了,很久没有来园子了,发上来当个在线笔记用吧,免的时间长了又忘了 ...
- C# String类&Math类&DateTime类
String类: String a = "abcdefghijklmnopqrstuvwxyz"; int length = a.length; //获取字符串的长度: a = ...
- react与umi
我们知道umi 是一个编译工具,但它同时也是一个前端框架.它对社区的 webpack,react-router 等进行的封装, 使得我们可以基于它快速搭建一个 React 项目. 第一步:安装umi ...
- A*算法(附c源码)
关于A*算法网上介绍的有很多,我只是看了之后对这个算法用c写了一下,并测试无误后上传以分享一下,欢迎指正!下面是我找的一个介绍,并主要根据这个实现的. 寻路算法不止 A* 这一种, 还有递归, 非递归 ...
- <!--more-->搭建的博客设置主页内容高度
用 markdown写文章时插入<!--more-->,文章会自动从插入的位置截断,也就是说在博客中只显示<!--more-->之前的内容,点击阅读全文之后会显示所有内容.
- Spring触发器触发2次问题【转】
触发2遍任务对大多数程序而言,都会造成 数据重复 和 资源浪费. 我们在写spring触发器的timetrigger.xml配置文件的时候, 千万不要在Spring的总配置文件applicationC ...
- POJ - 3267 The Cow Lexicon(动态规划)
https://vjudge.net/problem/POJ-3267 题意 给一个长度为L的字符串,以及有W个单词的词典.问最少需要从主串中删除几个字母,使其可以由词典的单词组成. 分析 状态设置很 ...