Java8闭包
闭包在很多语言中都存在,例如C++,C#。闭包允许我们创建函数指针,并把它们作为参数传递,Java编程语言提供了接口的概念,接口中可以定义抽象方法,接口定义了API,并希望用户或者供应商来实现这些方法,很多时候并不是为一些接口创建独立的实现类,我们通过写一个匿名的内部类来写一个内联的接口实现,匿名内部类使用相当的广泛,匿名内部类最常见的场景就是事件处理器了,其次匿名内部类还被用于多线程中,写匿名内部类而不是创建Runable\Callable接口的实现类。
一个匿名内部类就是一个内联的给定接口的实现,这个实现类的对象作为参数传递给一个方法,然后这个方法将在内部调用传递过来的实现类的方法,这种接口叫做回调接口,这个方法叫做回调方法。
匿名内部类很多地方都在使用,在使用的同时存在一些问题
复杂
这些类代码的层级看起来很乱很复杂,称作Vertical Problem
不能访问封装类的非final成员
this关键字变得很迷惑,如果一个匿名类有一个与其封装类相同的成员名称,内部类会覆盖外部的成员变量,在这种情况下,外部成员在匿名类内部是不可见的,甚至不能通过this来访问。
实例说明
public void test() {
String variable = "Outer Method Variable";
new Thread(new Runnable() {
String variable = "Runnable Class Member";
public void run() {
String variable = "Run Method Variable";
System.out.println("->" + variable);
System.out.println("->" + this.variable);
}
}).start();
}
输出->Run Method Variable
->Runnable Class Member这个例子很好的说明了上面的两个问题,而Lambda表达式几乎解决上面的所有问题,我们讨论Lambda表达式之前,让我们来看看Functional Interfaces
Funcational Interfaces
一个只有单个方法的接口,这代表了这个方法的契约。
The Single method cal exist in the form of multiple abstract methods that are inherited from superinterfaces.But in that case the inherited methods should logically represent a single method or it might redundantly declare a method that is provided by classes like Object,e.g.toString> interface Runnable{void run();}
> interface Foo {boolean equals(Object obj);}
> interface extends Foo{ int compare(String s1,String s2)}
> interface Comparetor{
boolean equals(Object obj);
int compare(T t1,T t2);
} > interface Foo( int m(); Object clone();大多数回调接口都是Functional Interfaces,例如Runable,Callable,Comparetor等等,以前被称作SAM(Single Abstract Method)
Lambda
- Lambda表达式实际上就是匿名类,只不过他们的结构更轻量,Lambda表达式看起来像方法。他们有一个正式的参数列表和这参数的块体表达式。针对上面的例子进行Lambda改造。
public class TestLambdaExpression{public String variable ="Class level variable";
public static void main(){new TestLambdaExpression().test();
}
public void test() {
String variable = "Method local Variable";
new Thread(() {
public void run() -> {
System.out.println("->" + variable);
System.out.println("->" + this.variable);
}
}).start();
}}
输出->Method local Variable
->Class level variable可以比较一些使用Lambda表达式和使用匿名内部类的区别,我们可以清楚的看出,使用Lambda表达式的方式写匿名内部类解决了变量可见性的问题,Lambda表达式不允许创建覆盖变量。 Lambda表达式的语法包括一个参数列表和“->”,为什么选择这样的语法形式,因为目前C#和Scala中通常都是这样的,也算是遵循了Lambda的通用写法,这样的语法基本上解决了匿名类的复杂性,同时也显得非常的灵活,目标接口类型不是一个表达式的一部分。编译器会帮助推断Lambda expression的类型与周围的环境,Lambda表达式必须有一个目标类型,而它们可以适配任意可能的目标类型,当然类型是一个接口的时候,下面的条件必须满足,才能编译正确。
接口应该是一个Funcational interface
表达式的参数数量和类型必须与Functional interface中声明的一致
抛出的异常表达式必须兼容function interface中方法的抛出异常声明
返回值类型必须兼容function interface 中方法的返回值类型
由于编译器可以通过目标类型的声明中得知参数类型和个数,所以在Lambda表达式中可以省略类型的声明。
例如
Comparetor c = (s1,s2) –> s1.comparteToIgnoreCase(s2);
而且,如果目标类型中声明的方法只有一个参数,那么小括号也可以不写,例如
ActionListenr listenr = event –> event.getWhen();
一个很明显的问题来了,为什么Lambda不需要指定方法名呢?因为Lambda expression只能用户Functional interface,而functional interface 只有一个方法。当我们确定一个functional interface来创建Lambda表达式的时候,编译器可以感知functional interface中的方法的签名,并且检查给定的表达式是否匹配。Lambda表达式的语法是上下文相关的,但并非在Java8中才出现,Java 7中添加了diamond operators也有这个概念,通过上下文推断类型。
void invoke (Runable r) {r.run()}
Futrue invoke (Callable c) {return c.compute()}
Future s = invoke (() –> "done");//这个Lambda Expression显然是调用了带有futrue的这个接口
方法引用
方法引用被用作引用一个方法而不调用它,Lambda表达式允许我们定义一个匿名的方法,并将它作为Functional Inteface的一个实例。方法引用跟Lambda Expression很想,它们都需要一个目标类型,但是不同的方法引用不提供方法的实现,它们引用一个已经存在的类或者对象的方法。
1、System::getProperty
2、"abc"::length
3、String::length
4、super::toString
5、Arraylist::new
这里引用了一个新的操作符"::"(双冒号)。目标引用或者说接收者被放在提供者和分隔符的后面,这形成了一个表达式,它能够引用一个方法。下面通过一个例子来了解这个操作符。
这是一个Employee数组的排序程序
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class MethodReference {
public static void main (String[] ar){
Employee[] employees = {
new Employee("Nick"),
new Employee("Robin"),
new Employee("Josh"),
new Employee("Andy"),
new Employee("Mark")
};
System.out.println("Before Sort:");
dumpEmployee(employees);
Arrays.sort(employees, Employee::myCompare);
System.out.println("After Sort:");
dumpEmployee(employees);
}
public static void dumpEmployee(Employee[] employees){
for(Employee emp : Arrays.asList(employees)){
System.out.print(emp.name+", ");
}
System.out.println();
}
}
class Employee {
String name;
Employee(String name) {
this.name = name;
}
public static int myCompare(Employee emp1, Employee emp2) {
return emp1.name.compareTo(emp2.name);
}
}输出:
- Before Sort: Nick, Robin, Josh, Andy, Mark,
- After Sort: Andy, Josh, Mark, Nick, Robin,
构造方法引用
先看看实例
public class ConstructorReference {
public static void main(String[] ar){
MyInterface in = MyClass::new;
System.out.println("->"+in.getMeMyObject());
}
}
interface MyInterface{
MyClass getMeMyObject();
}
class MyClass{
MyClass(){}
}输出
->com.MyClass@34e5307e
这看起来有点神奇吧,这个接口和这个类除了接口中声明的方法的返回值是MyClass类型的,没有任何关系。这个例子又激起了我心中的另一个问题:怎样实例化一个带参数的构造方法引用?看看下面的程序:
public class ConstructorReference {
public static void main(String[] ar){
EmlpoyeeProvider provider = Employee::new;
Employee emp = provider.getMeEmployee("John", 30);
System.out.println("->Employee Name: "+emp.name);
System.out.println("->Employee Age: "+emp.age);
}
}
interface EmlpoyeeProvider{
Employee getMeEmployee(String s, Integer i);
}
class Employee{
String name;
Integer age;
Employee (String name, Integer age){
this.name = name;
this.age = age;
}
}输出是:
->Employee Name: John
->Employee Age: 30Default Method
Java8中将会引入一个叫做默认方法的概念,早期的Java版本的接口拥有非常的严格的接口,接口包含了一些抽象方法的声明,所有非抽象的实现类必须要提供所有这些抽象方法的实现,甚至是这些方法没有用或者不合适出现在一些特殊的实现类中。在即将到来的Java 版本中,允许我们在接口中定义方法的默认实现。
public class DefaultMethods {
public static void main(String[] ar){
NormalInterface instance = new NormalInterfaceImpl();
instance.myNormalMethod();
instance.myDefaultMethod();
}
}
interface NormalInterface{
void myNormalMethod();
void myDefaultMethod () default{
System.out.println("-> myDefaultMethod");
}
}
class NormalInterfaceImpl implements NormalInterface{
@Override
public void myNormalMethod() {
System.out.println("-> myNormalMethod");
}
}输出
-> myDefaultMethod
在这个例子中,ParentInterface 定义了两个方法,一个是正常的,一个是有默认实现的,子接口只是简单的反了过来,给第一个方法添加了默认实现,给第二个方法移除了默认实现。
设想一个类继承了类 C ,实现了接口 I ,而且 C 有一个方法,而且跟I中的一个提供默认方法的方法是重载兼容的。在这种情况下,C中的方法会优先于I中的默认方法,甚至C中的方法是抽象的时候,仍然是优先的。
public class DefaultMethods {
public static void main(String[] ar){
Interfaxe impl = new NormalInterfaceImpl();
impl.defaultMethod();
}
}
class ParentClass{
public void defaultMethod() {
System.out.println("->ParentClass");
}
}
interface Interfaxe{
public void defaultMethod() default{
System.out.println("->Interfaxe");
}
}
class NormalInterfaceImpl extends ParentClass implements Interfaxe{
}输出:
-> ParentClass
第二个例子是,实现了两个不同的接口,但是两个接口中都提供了相同的具有默认实现的方法的声明。在这种情况下,编译器将会搞不清楚怎么回事,实现类必须选择两个的其中一个实现。这可以通过如下的方式来使用super来搞定。
public class DefaultMethods {
public static void main(String[] ar){
FirstInterface impl = new NormalInterfaceImpl();
impl.defaultMethod();
}
}
interface FirstInterface{
public void defaultMethod() default{
System.out.println("->FirstInterface");
}
}
interface SecondInterface{
public void defaultMethod() default{
System.out.println("->SecondInterface");
}
}
class NormalInterfaceImpl implements FirstInterface, SecondInterface{
public void defaultMethod(){
SecondInterface.super.defaultMethod();
}
}输出
->SecondInterface
Java8闭包的更多相关文章
- Java8函数接口实现回调及Groovy闭包的代码示例
本文适用于想要了解Java8 Function接口编程及闭包表达式的筒鞋. 概述 在实际开发中,常常遇到使用模板模式的场景: 主体流程是不变的,变的只是其中要调用的具体方法. 其特征是: Begi ...
- Java8-Function使用及Groovy闭包的代码示例
导航 定位 概述 代码示例 Java-Function Groovy闭包 定位 本文适用于想要了解Java8 Function接口编程及闭包表达式的筒鞋. 概述 在实际开发中,常常遇到使用模板模式的场 ...
- Java8的新特性以及与C#的比较
函数式接口 VS 委托 在C中,可以使用函数指针来存储函数的入口,从而使得函数可以像变量一样赋值.传递和存储,使得函数的调用变得十分灵活,是实现函数回调的基础.然而函数指针不存在函数的签名信息,甚至可 ...
- JAVA8 十大新特性详解
前言: Java8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章, 例如Playing with Java ...
- java8新特性全面解析
在Java Code Geeks上有大量的关于Java 8 的教程了,像玩转Java 8--lambda与并发,Java 8 Date Time API 教程: LocalDateTime和在Java ...
- Java8新特性【转】
地址:http://ifeve.com/java-8-features-tutorial/ 1.简介 毫无疑问,Java 8是自Java 5(2004年)发布以来Java语言最大的一次版本升级,Ja ...
- java8 新特性
[转载]:http://www.importnew.com/11908.html 本文由 ImportNew - 刘 家财 翻译自 javacodegeeks.欢迎加入翻译小组.转载请见文末要求. 编 ...
- lambda表达式和闭包
lambda表达式和闭包 熟悉的Javascript或者Ruby的同学,可能对另一个名词:闭包更加熟悉.因为一般闭包的示例代码,长得跟lambda差不多,导致我也在以前很长一段时间对这两个概念傻傻分不 ...
- Java8 Lamdba表达式 001
在一个已经存在的编程语言里非常少有对现有的生态系统起重大影响的新特性.Lambda表达式对于Java语言就是这样的意义的存在.简单来说,Lambda表达式提供了便利的方式去创建一个匿名的功能.提供了一 ...
随机推荐
- true_kb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Windows下wnmp相关配置
#wnmp mysqld -install net start mysql memcached -d uninstall memcached -d install net start memcache ...
- openstack中eventlet使用
openstack中使用eventlet的协程来实现并发. 第一种,使用eventlet.GreenPool来管理绿色线程 如l3-agent在开启了8个绿色线程来处理router消息 def _pr ...
- python httprequest, locust
r = self.client.get("/orders", headers = {"Cookie": self.get_user_cookie(user[0] ...
- DNS解析流程
DNS简单来说就是进行域名和IP的转换,那该如何转换呢?既然要转换,肯定有转换表,那表应该存 哪个服务器上,怎样去请求域名服务器来进行转换,所以,这个转换的过程都是什么.而面试的时 经常会有这道题:当 ...
- Libgdx 循环绘制图片时间隔的问题
在libgdx中使用循环绘制一张图片铺满某个区域时,有可能会遇到像素计算没有问题时,图块中间还是有约1像素的间隔,或者是本来没有间隔,做了缩放处理之后发现中间有间隔. 解法 当使用Texture加载图 ...
- 手势响应 ,避免点击多个cell同时响应同一手势多次,只响应第一个cell
http://www.cnblogs.com/wfwenchao/articles/3700205.html UIView除了负责展示内容给用户外还负责响应用户事件.本章主要介绍UIView用户交互相 ...
- ORA-29275: partial multibyte character
查询表报错 修改方式1 和字符集存储方式有关系 ,修改客户端和服务器的字符集存储方害死 修改方式2 修改表的字段由nvarchar2修改为varchar2
- Hyper-V 与Broadcom网卡兼容问题
最近在测虚拟机时,碰到一个网卡和Hyper-V不兼容问题,现在共享给大家参考,希望对大家有帮忙. 故障描述: Dell R720 Windows 2012操作系统下的Hyper-V环境后,虚拟机网络速 ...
- rpm与yum
设置本地源与网络源:https://my.oschina.net/u/1861462/blog/723958 rpm命令:http://man.linuxde.net/rpm https://my.o ...