lambda表达式学习二

学习总结自《java核心技术卷一》
一.函数式接口

1.什么是函数式接口?
对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式替代对象,这样的接口我们称之为函数式接口(functional interface)。 (有的接口可能重新声明Object类的方法,这些声明可能会让方法不再是抽象的,且在javaSE8中,接口可以声明非抽象方法)

2.如何用lambda表达式替代?
我们以下面的例子来理解:

Arrays.sort(words,(first,second)->first.length()-second.length());

上面使用了Arrays.sort()方法,它的第二个参数需要一个Comparator的实例,而Comparator就是只有一个方法的接口,所以我们可以用lambda表达式 (first,second)->first.length()-second.length() 替代它

我们要记住:把lambda表达式看作是一个函数,而且lambda表达式可以传递到函数式接口中去

实际上,在java中lambda表达式所能做的也只能是转换为函数式接口:

Timer t= new Timer(1000,evnet->
{
  System.out.println("At the tone,the time is"+new Date());
  Toolkit.getDefaultToolkit().beep();
});

上面代码与使用实现了ActionListener接口的类相比,这个代码可读性要好很多。

3.Java API在java.util.function包中定义了很多非常通用的函数式接口。
在这里插入图片描述
4.下表 列出了基本类型 int、 long 和 double 的 34 个可能的规范。 最好使用这些特殊 化规范来减少自动装箱。出于这个原因, 我在上一节的例子中使用了 IntConsumer 而不是 Consumer< lnteger >:

在这里插入图片描述

5.想要用lambda表达式做某些处理,还是要谨记表达式的用途,为它建立一个特定的函数式接口
例如:java.util.function包中有一个接口:

public interface Predicate<T>
{
 boolean test(T t);
}

ArrayList类有一个removeIf方法,它的参数就是一个Predicate。这个接口专门用来传递lambda表达式。如:

list.removeIf(e->e==null);

上面的语句表示从一个数组列表删除所有的null值。

二.方法引用

1.有时我们可能已经有现成的方法可以完成你想要传递到其他代码的某个动作。

例如:假设我们只希望只要出现一个定时器事件就打印这个事件的对象。
为此我们可以调用:

Timer t=new Timer(1000,event->System.out.println(event));

但有咩有什么方法可以直接把println方法传递到Timer构造器呢?
答案是有:

Timer t=new Timer(1000,System.out::Println);//等价于上面的语句

2.从上面的例子我们可以看出,要用::操作符分隔方法名与对象或类名,主要有三种情况:

1)object::instanceMethod
2)Class::staticMethod
3)Class::instanceMethod

前两种情况中,方法的引用等价于提供方法参数的lambda表达式,例如:

System.out::println等价于x->System.out.println(x)
Math::pow等价于(x,y)->Math.pow(x,y).

第三种情况种,第一个参数会成为方法的目标,例如:

String::compareToIgnoreCase等价于(x,y)->x.compareToIgnoreCase(y)

注意:我们也可以在方法中引用this参数和super。

三.构造器引用

构造器引用与方法引用很类似,只不过方法名为new。
例如:Person::new 是Person构造器的引用。

四.变量作用域

1.通常,我们希望能够在lambda表达式中访问外围方法或类中的变量。

下面我们进一步学习lambda表达式的几个部分:

1.一个代码块
2.参数
3.自由变量的值,这是指非参数而且不在代码中定义的变量。

2.在下面情况下lambda表达式才可以引用外围变量:

1).引用值是一直不会改变的变量。
2).在lambda表达式中声明一个与局部变量同名的参数或局部变量是不合法的。

3.在lambda表达式中使用this关键字时,是指创建这个lambda表达式的this参数。

例如:

public class Application()
{
 public void init()
 {
  ActionListener listener=event->
  {
    System.out.println(this.toString());
  }
 }
}

表达式this.toString()会调用的是Application对象的toString方法。

五.处理lambda表达式
通过前面的学习,我们已经直到如何生成lambda表达式,以及如何把lambda表达式传递到需要一个函数式接口的方法。

所以我们下面需要学习如何编写方法处理lambda表达式:
1.我们要明白使用lambda表达式的重点是延迟执行(deferred execution)。 因为如果我们想要立即执行代码,我们可以直接执行,没有必要把它包装在一个lambda表达式中。

之所以要延迟执行,一般有以下原因:
1)在一个单独的线程中运行代码。
2)多次运行代码。
3)在算法的适当位置运行代码。
4)发生某种情况时执行代码。
5)只在必要时才运行代码。

下面我们通过例子来学习:
假设我们想要重复一个动作n次,我们可以:

repeat(10,()->System.out.println("Hello,World"));

public static void repeat(int n,Runnable action)
{
  for(i=0;i<n;i++) action.run();//调用action.run()时会执行上面的lambda表达式的主体
}

下面我们上这个例子更复杂些,我们希望告诉这个动作它出现在哪一次迭代中,我们可以:

public interface IntConsumer
{
 void accept(int value);
}
public static void repeat(int n,IntConsumer action)
{
  for(int i=0;i<n;i++) action.accept(i);
}

repeat(10,i->System.out.println("Countdown"+(9-i)));

猜你喜欢

转载自blog.csdn.net/c1776167012/article/details/106326008
今日推荐