Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
在 Java 语言中,可以为变量赋予一个值。
能否把一个代码块赋给一变量吗?
在 Java 8 之前,这个是做不到的。但是 Java 8 问世之后,利用 Lambda 特性,就可以做
到了。
甚至我们可以让语法变得更简洁。
这样,我们就成功的非常优雅的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个 Lambda 表达式。但是这里仍然有一个问题,就是变量 aBlockOfCode 的类型应该是什么?
在 Java 8 里面,所有的 Lambda 的类型都是一个接口,而 Lambda 表达式本身,也就是” 那段代码“,需要是这个接口的实现。这是我认为理解 Lambda 的一个关键所在,简而言之
就是,Lambda 表达式本身就是一个接口的实现。直接这样说可能还是有点让人困扰,我们
继续看看例子。我们给上面的 aBlockOfCode 加上一个类型:
这种只有一个接口函数需要被实现的接口类型,我们叫它”函数式接口“。为了避免后来的人在这个接口中增加接口函数导致其有多个接口函数需要被实现,变成"非函数接口”,我们可以在这个上面加上一个声明@FunctionalInterface, 这样别人就无法在里面添加新的接口函数了。
虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。
jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。
@FunctionalInterface 标记在接口上,“函数式接口”是指仅仅只包含一个抽象方法的接口。
语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda 运算符 ,读作(goes to)。
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
//Lambda表达式入门案例_定义函数接口
//无返回值,无参数
@FunctionalInterface
interface NoReturnNoParam{
void method();
}
//无返回值,有一个参数
@FunctionalInterface
interface NoReturnOneParam{
void method(int a);
}
//无返回值,有多个参数
@FunctionalInterface
interface NoReturnMultiParam{
void method(int a,int b);
}
//有返回值,无参数
@FunctionalInterface
interface ReturnNoParam{
int method();
}
//有返回值,有一个参数
@FunctionalInterface
interface ReturnOneParam{
int method(int a);
}
//有返回值,有多个参数
@FunctionalInterface
interface ReturnMultiParam{
int method(int a,int b);
}
public class Test {
}
public class Test {
//Lambda表达式入门案例_实现函数接口
public static void main(String[] args) {
//实现无返回值,无参数接口
NoReturnNoParam noReturnNoParam = ()->{
System.out.println("NoReturnNoParam");
};
noReturnNoParam.method();
//实现无返回值,有一个参数接口
NoReturnOneParam noReturnOneParam = (int a)->{
System.out.println("NoReturnOneParam "+a);
};
noReturnOneParam.method(1);
//实现无返回值,有多个参数接口
NoReturnMultiParam noReturnMultiParam =(int a,int b)->{
System.out.println("NoReturnMultiParam "+a+" "+b);
};
noReturnMultiParam.method(1,2);
//实现有返回值,无参数接口
ReturnNoParam returnNoParam = ()->{
System.out.println("ReturnNoParam");
return 10;
};
System.out.println(returnNoParam.method());
//实现有返回值,有一个参数接口
ReturnOneParam returnOneParam = (int a)->{
System.out.println("ReturnOneParam");
return a;
};
System.out.println(returnOneParam.method(20));
//实现有返回值,有多个参数接口
ReturnMultiParam returnMultiParam = (int a,int b)->{
System.out.println("ReturnMultiParam" );
return a+b;
};
System.out.println(returnMultiParam.method(10,20));
}
}
简化版
/*简化版*/
public class Test {
//Lambda表达式入门案例_实现函数接口
public static void main(String[] args) {
//实现无返回值,无参数接口
NoReturnNoParam noReturnNoParam = ()-> System.out.println("NoReturnNoParam");
noReturnNoParam.method();
//实现无返回值,有一个参数接口
NoReturnOneParam noReturnOneParam = a-> System.out.println("NoReturnOneParam "+a);
noReturnOneParam.method(1);
//实现无返回值,有多个参数接口
NoReturnMultiParam noReturnMultiParam =(a,b)-> System.out.println("NoReturnMultiParam "+a+" "+b);
noReturnMultiParam.method(1,2);
//实现有返回值,无参数接口
ReturnNoParam returnNoParam = ()->{
System.out.println("ReturnNoParam");
return 10;
};
System.out.println(returnNoParam.method());
//实现有返回值,有一个参数接口
ReturnOneParam returnOneParam = a->{
System.out.println("ReturnOneParam");
return a;
};
System.out.println(returnOneParam.method(20));
//实现有返回值,有多个参数接口
ReturnMultiParam returnMultiParam = (a,b)->{
System.out.println("ReturnMultiParam" );
return a+b;
};
System.out.println(returnMultiParam.method(10,20));
}
}
有时候我们不是必须使用 Lambda 的函数体定义实现,我们可以利用 lambda 表达式指
向一个已经被实现的方法。
语法如下:
方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象。
将外部方法作为函数接口抽象方法的一个实现方法的案例:
public class Test2 {
public static void main(String[] args) {
ReturnOneParam returnOneParam = a -> doubleNum(a);
int value = returnOneParam.method(10);
System.out.println(value);
}
/*
* 将外部方法作为函数接口抽象方法的一个实现方法的要求:
* 1.参数的个数以及类型需要与函数接口中的抽象方法一致。
* 2.返回值类型要与函数接口中的抽象方法的返回值类型一致。*/
public static int doubleNum(int a){
return 2*a;
}
}
如果该方法不在此类中,在另一个类(Test)中时,语法结构为:
z方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象。
静态方法public static int doubleNum(int a){ return 2*a; }
静态方法的归属者为类名:
ReturnOneParam returnOneParam = Test::doubleNum;
非静态方法public int addTwo(int a){ return a+2; }
普通方法归属者为对象:
需要先new一个对象:
//非静态方法要先实例化对象
Test test = new Test();
ReturnOneParam returnOneParam1 = test::addTwo;
int value1 = returnOneParam1.method(10);
System.out.println(value1);
在Java的多线程中,对于线程的创建有两种方式,第一种是通过继承Thread类,并且重写里面的run方法来定义我们的线程类。第二种方法是实现Runnable接口,然后再实现其中的run方法。
在Runnable中只包含一个抽象方法,符合我们函数接口的定义,所以可以使用Lammbda表达式去做这个接口当中抽象方法的具体实现。
所以,Lambda表达式的作用就是对Runnable接口中的run方法的具体实现
代码如下:
//Lambda表达式的使用_创建线程
public class Test3 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+" 开始");
/*
* new Thread(a,"b")方法的第一个参数是当前这个接口实现类的引用
* 也可以给当前线程起个名字作为第二个参数*/
//方式一:
// Runnable runnable = ()->{};
// new Thread(runnable,"Lambda Thread ").start();;
//简写:
new Thread(()->{
//大括号中定义子线程要做的工作,此处用打印语句表示
for(int i=0;i<20;i++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
},"Lambda Thread ").start();
System.out.println(Thread.currentThread().getName()+" 结束");
//Lambda表达式的作用就是对Runnable接口中的run方法的具体实现
}
}
list.forEach(System.out::println);
public class Test4 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.forEach(System.out::println);
}
}
点击查看详细讲解
list.removeIf(ele->ele.equals(“b”));
我们通过List接口下的 public boolean removeIf(Predicate super E> filter)方法来删除集合中的某个元素,在此方法中有一个参数,该参数为Predicate类型, Predicate 是 jdk 为我们提供的一个函数式接口
我们需要对其中的isEqual方法进行实现,让它去对我们要去做删除的这个元素的一个判断条件的给定。所以我们可以利用Lambda表达式对这个方法进行实现。(ele为自定义的参数的名字)
//Lambda表达式的使用_操作集合_删除元素
public class Test5 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.removeIf(ele->ele.equals("b"));
list.forEach(System.out::println);
}
}
list.sort(((o1, o2) -> o1.compareTo(o2)));
之前我们若要为集合内的元素排序,就必须调用 sort 方法,传入比较器重写 compare
方法的比较器对象,现在我们还可以使用 lambda 表达式来简化代码。
在List接口下的sort方法需要一个元素比较器Comparator
所以,我们先要定义一个Comparator比较器,并对里面的方法进行实现
正常操作:
//操作集合_元素排序
/*
* 首先。我们要定义一个Comparator比较器,并对里面的方法进行实现*/
class ComparatorImpl implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}
public class Test6 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("d");
list.add("c");
list.add("b");
list.sort(new ComparatorImpl());
list.forEach(System.out::println);
}
}
简化版:利用Lambda表达式来对Comparator方法进行实现。
public class Test6 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("d");
list.add("c");
list.add("b");
list.sort(((o1, o2) -> o1.compareTo(o2)));
list.forEach(System.out::println);
}
}
什么是闭包?
闭包的本质就是代码片断。所以闭包可以理解成一个代码片断的引用。在 Java 中匿名内部类也是闭包的一种实现方式。
在闭包中访问外部的变量时,外部变量必须是 final 类型,虚拟机会帮我们加上 final 修饰关键字。
//Lambda表达式的使用_闭包问题
public class Test7 {
public static void main(String[] args) {
/*
* 如果我们想要去实现一个接口,我们可以使用匿名内部类*/
int num = 10;
NoReturnNoParam noReturnNoParam = new NoReturnNoParam() {
@Override
public void method() {
System.out.println(num);
}
};
noReturnNoParam.method();
}
}
简化版:
//Lambda表达式的使用_闭包问题
public class Test7 {
public static void main(String[] args) {
/*
* 如果我们想要去实现一个接口,我们可以使用匿名内部类*/
int num = 10;
NoReturnNoParam noReturnNoParam = ()->System.out.println(num);
noReturnNoParam.method();
}
}