第一章 lambda表达式

Java8主要是在原来面向对象的基础上增加了函数式编程的能力
一.为什么要使用lambda
1.长久以来,在java中向其他代码传递一段代码并不是很容易。由于是面向对象的语言,我们不得不构建一个属于某个类的对象,由它的某个方法来包含所需的代码,如多线程任务中,Runnable接口中的run方法,排序时Comparator接口的compare方法。
以下是几个常见例子
------------------------------------------------------线程任务--------------------------------------------------
class Worker implements Runnable{
public void run(){
for(int i=0;i<1000;i++){
dowork();
}
}
……
}
Worker worker=new Worker();
new Thread(worker).start();
--------------------------------------------------------排序------------------------------------------------------
class LengthComparator implements Compatator<String>{
public int conpare(String first,String second){
return Integer.compare(first.length(),second.length());
}
}
Arrays.sourt(strings,new LengthComparator());

注意:
Integer.compare(x,y)是JAVA7添加的静态方法
另外,不应该直接使用x-y来比较x和y的大小,因为对于大的、符号相反的操
作数,这种计算可能会产生溢出
--------------------------------------------------------------------------------------------------------------------
button.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent event){
System.out.println("Thank for clicking!");
}
})
--------------------------------------------------------------------------------------------------------------------
2.lambda表达式的出现给Java带来了新的传递方式,lambda表达式是一段可以传递的代码,因此可以被执行一次或多次

二、lambda表达式的语法
1.第一个lambda表达式
(String first,String second)->Integer.compare(first.length(),second.length())
2.如果负责计算的代码无法用一个表达式表示,可以用编写方法的方式来编写
(String first,String second)->{
if(first.length()<second.length()){
return -1;
}else if(first.length()>second.length()){
return 1;
}else{
return 0;
}
}
3.如果lambda表达式可以没有参数
()->{
for (int i = 0; i < 1000; i++){
doWork();
}
}
4.如果一个lambda表达式的参数类型式可以被推导的,那么就可以省略它们的类型
Comparator<String> comp=
(first,second)->Integer.compare(first.length(),second.length()) ;
5.如果某个方法只含一个参数,并且该参数的类型可以被推导出来,则可省略小括号
EventHandler<ActionEvent> listener = event->System.out.println("thanks!") ;
6.永远不需要为一个lambda表达式执行返回类型,它总会从上下文中被推导出来
7.lambda表达式若有一个分支有返回值,则所有分支都应该有返回值
(int x)->{ if(x>=0) return 1; } //不合法
8.可以向对待方法参数一样向lambda表达式的参数添加注解或final修饰符
(final String name)->……
(@NonNull String name)->……

三、函数式接口
1.对于只包含一个抽象方法的接口,称为函数式接口,可以通过lambda表达式来创建接口的对象。这里可能会存在疑惑"接口的方法不都是抽象的吗?",事实上接口经常会重新声明Object类中的方法,如toString,clone,而这些方法并不是抽象的,另外java8中接口有了更多的特性
2.Java8中接口可以声明非抽象的方法
3.在Java中lambda表达式能做的唯一一件事就是函数式接口的转换
(1).Arrays.sort(words,
(first,second)->Integer.compare(first.length(),second.length())) ;
(2).button.setOnAction( event->System.out.println("Thank") );
4.建议在函数式接口上标注 @FunctionalInterface 注解,有两点好处
(1).编译器会检查标注该注解的实体,检查它是否是只包含一个抽象方法的接口
(2).javadoc页面会包含一条声明,说明这个接口是一个函数式接口
5.当lambda表达式可能会抛出一个检查期异常时,要么在表达式中捕获并进行处理,要么lambda表达式只能赋给一个其抽象方法已声明抛出异常的接口

四、方法引用
有些时候,你想要传递给其他代码的操作已经有实现的方法,此时如果能直接传递已实现的方法就更妙了,例如
--------------------------------------------------------------------------------------------------------------------
button.setOnAction( System.out::println );
--------------------------------------------------------------------------------------------------------------------
表达式 System.out::println 是一个方法引用等同于 x->System.out.println(x)
1. :: 操作符将方法和对象或类的名字分隔开来,主要有三种使用情况
(1).对象 :: 实例方法
(2).类 :: 静态方法
(3).类 :: 实例方法
前两种情况中,方法引用等同于提供方法参数的lambda表达式
System.out::println 等同于 x->System.out.println(x)
Math::pow 等同于 (x,y)->Math.pow(x,y)
第三种情况,第一个参数会成为执行方法的对象
String::compareToIgnoreCase 等同于 (x,y)->x.compareToIgnoreCase(y)
2.this::equals(x) 等同于 x->this.equals(x)
3.super::实例方法
会使用this作为执行方法的对象,并调用父类中指定的方法
4.同lambda表达式类似,方法引用也不会独立存在,它们经常被用于转换为函数式接
口的实例

五、构造器引用
1.构造器引用同方法引用类似,不同的是在构造器引用中方法名是new,对于拥有多个构造器的类,选择使用哪个构造器取决于上下文
int[]::new 等同于 x->new int[x]
2.数组构造器引用可以用来绕过Java中的一个限制
在Java中,无法构造一个泛型类型T的数组,表达式new T[n]是错误的,因为它会被擦除为new object[n]。假设我们希望构造一组按钮,Stream接口中有一个返回Object数组的toArray()方法
--------------------------------------------------------------------------------------------------------------------
Object[] buttons=Stream.toArray();
--------------------------------------------------------------------------------------------------------------------
这并不能让我们满意,我们希望的是得到一组按钮对象而不是一组Object对象。Stream API通过构造器引用解决了这个问题,它允许将 Button[]::new 传递给toArray方法
--------------------------------------------------------------------------------------------------------------------
Button[] buttons=Stream.toArray( Button[]::new );
--------------------------------------------------------------------------------------------------------------------

六、变量作用域
1.一个lambda表达式包括三个部分
(1).一段代码
(2).参数
(3).自由变量:不是lambda参数并且没有在lambda代码块中定义的变量
--------------------------------------------------------------------------------------------------------------------
public static void repeatMessage(String text,int count){
Runnable r = () ->{
for( int i=0;i<count;i++){
System.out.println(text);
Thread.yield();
}
};
new Thread(r).start();
}
注:lambda表达式中的count与text就是自由变量,数据结构表示lambda表达式必须存储这两个变量的值,即"Hello"和1000。我们可以说,这些值已经被lambda表达式捕获了
--------------------------------------------------------------------------------------------------------------------
2.lambda表达式中,被引用的变量(自由变量)的值不可以被更改
3.含有自由变量的代码块成为“闭包”,在java中,lambda表达式就是闭包。此外,内部类也是闭包
4.在Java8之前,内部类只允许访问final的局部变量,但在Java8之后,为了适应lambda表达式,内部类可以访问任何"有效的final局部变量"(任何值不会发生变化的变量)
5.lambda表达式的方法体与嵌套代码块有着相同的作用域,因此在lambda表达式中不允许声明一个与局部变量同名的参数或者局部变量
6.与在其他地方使用this没有什么不同,当你在lambda表达式中使用this关键字时,你会引用创建该lambda表达式的方法的this参数
--------------------------------------------------------------------------------------------------------------------
public class Application{
public void doWork(){
Runnable runner=()->{...; System.out.println(this.toString()); ...};
……
}
}
--------------------------------------------------------------------------------------------------------------------
上例中的this.toString()会调用Application对象的toString方法,而不是Runner实例的toString方法

七、默认方法
1.Java8之后,接口加入了默认方法,这里说说默认方法的来由,因为Java的集合库是许多年以前设计的,在API开发人员试图对接口如Collection添加新的方法时,就必须让每个实现了Collection接口的自定义类都实现该方法,这在Java中式完全无法接受的。
其中list.forEach(System.out::println)就是java8新添的方法
--------------------------------------------------------------------------------------------------------------------
interface Person{
long getId();
default String getName(){
return "OJBK";
}
}
--------------------------------------------------------------------------------------------------------------------
实现Person接口的具体类必须实现getId方法,但是它可以选择保留getName的
实现或重写
2."类优先":如果一个类同时继承了一个父类,实现了一个接口,当父类以及接口中具有相同名字和参数的方法时,无论接口中的方法是否是默认方法,会优先继承父类方法
3.接口冲突:如果一个父接口提供了一个默认方法,而另一个接口也提供了一个具有相同名字和参数的方法(不管是否是默认方法),那么必须通过覆盖该方法来解决冲突
--------------------------------------------------------------------------------------------------------------------
class Student implements Person,Named{
public String getName(){
return Person.super.getName();//这里选择调用Person的getName方法
}
}
--------------------------------------------------------------------------------------------------------------------
4.如果两个接口都没有提供默认方法,则属于Java8之前的正常情况,实现类可以选择实现该方法或者不实现该方法,如果是后者,则实现类就是个抽象类
5.由于"类优先"规则的存在,所以不能为Object类中的存在的方法定义一个默认方法

八、接口中的静态方法
1.在Java8中,你可以为接口添加静态方法,因此在Java8中,其实很多工具类中的方法可以定义在接口中,如Collections、Paths等,虽然Java的集合类不太可能这样去重构
2.在Java8中,很多接口已经添加了静态方法
(1).List.nCopies(int n,T o):public static <T> List<T> nCopies(int n,T o)
构造含有n个O类型实例的列表
(2).Comparator.comparing(键提取函数)
接收一个键提取函数并产生一个用来比较所提取出的键的比较器
Comparator.comparing(Person::get name)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值