一、什么是Lambda?
在Java中使用面向对象思想时强调,通过对象的思想,创建、实例化、调用对象等方式解决问题。Lambda的本质上是匿名方法,可以理解为简洁的匿名函数。Java8提供了更为简洁的语法,可以用表达式来替代函数式接口,使得代码更为简洁明了。
下面通过三种方法来对比之间的区别;
需求:创建并启动线程,在控制台输出"线程已执行;
方法① 实现Runnable接口,重写run()方法;
//定义接口实现类ThreadImp;
public class ThreadImp implements Runnable(){
@Override
public void run(){
System.out.println("线程已被启动")
}
}
//主方法调用;
public class LambdaDemo{
public static void main (String[] args){
ThreadImp Thp = new ThreadImp();
Thread t1 = new Thread(Thp);
t1.Start();
}
}
方法② 匿名内部类;
public class LambdaDemo{
public static void main (String[] args){
new Thread(new Runnable(){
@Override
public void run(){
System.out.println("匿名内部类线程已启动");
}).start();
}
}
方法③ Lambda表达式
public class LambdaDemo{
public static void main (String[] args){
new Thread( ( ) -> {
System.out.println("Lambda线程已启动")
}).start();
}
}
这样一对比!!Lambda表达式的代码确实简洁了许多,但是Lambda是如何使用的呢?让我们来分析一下:
<Lambda代码块分析>
组成: (形参) -> { 代码块 }
( ) : 里面没有内容,可以看做是方法形式参数为空;
-> : 箭头后面表示要做的事情;
{ } : 代码块,方法体中的内容;
组成Lambda的三要素: 形参、箭头、代码块;
演示三种情况下Lambda的使用
抽象方法无参无返回值
public class LambdaDemo{
public static void main (String[] args){
useEatable( ( ) -> {
System.out.println("今天也是干饭人!")
});
}
public interface Eatable{
void eat();
}
public stitac void useEatable(Eatable e){
e.eat();
}
}
控制台输出:今天也是干饭人!
抽象方法带参无返回值
public class LambdaDemo{
public static void main (String[] args){
useEatable( (String s) -> {
System.out.println(s)
});
}
public interface Eatable{
void eat(String s);
}
public stitac void useEatable(Eatable e){
e.eat("晚上要早点休息");
}
}
控制台输出: 晚上要早点休息
抽象方法带参有返回值
public class LambdaDemo{
public static void main (String[] args){
useEatable( (int x,int y) -> {
return x+y;
});
}
public interface addAble{
int add(int x,int y);
}
public stitac void useAddable(addAble a){
int sum = a.add(10,20);
System.out.println(sum)
}
}
控制台输出:30
看到这里相比对Lambda表达式可以是代码更简洁有了一定的概念吧,但是!Lambda表达式还有更为省略的模式;
// 正常Lambda表达式;
useAdd((int x,int y)->{
return x+y;
})
// ① 省略参数(有多个参数的情况下不可只省略一个,要么全部省略,要么全部留下)
useAdd((x,y)->{
return x+y;
})
// ② 只有一条一句的情况下可以把 ; 、 () 、{} 省略;
useEat(s -> System.out.printnl(s));
// ③ 只有一条语句的情况下而且还有return,可以省略return;
useAdd((x,y) -> x+y);
现在回头想一下Lambda表达式到底有什么作用呢?
答:最直观的就是使得代码块变得非常非常简洁;
这两种方式的输出结果是一样的,相比于Java7的传统写法,Java8提供的Lambda的代码界面是非常干净的;
使用Lambda的时候需要记住:
1. Lambd返回的是接口的实例对象;
2. 返回值类型,参数,参数个数需要选择自己合适的函数式接口;
Lambda与匿名内部类的区别:
1. 所需要的类型不同;
* 匿名内部类:接口、抽象类、具体类;
* Lambda:接口;
2. 使用的限制不同;
* 接口只有一个抽象方法时候,两者都可以使用;
* 接口有多个抽象方法的时候,只能使用匿名内部类;
3. 实现的原理不同;
* 匿名内部类:编译后会产生新的.class字节码文件;
* Lambda:编译后没有产生新的字节码文件,对应的字节码在运行的时候动态生成;
二、方法引用;
序号 | 引用方法 | 格式 |
---|---|---|
① | 类方法 | 类名 : : 静态方法 |
② | 对象的实例化方法 | 对象 : : 成员方法 |
③ | 类的实例方法 | 类名 : : 成员方法 |
④ | 构造器 | 类名 : : new |
- 引用类方法:
public class ConverterDemo {
public static void main(String[] args) {
//Lambda;
useNumber(i -> Integer.parseInt(i));
//引用类方法;类名::静态方法
useNumber(Integer::parseInt);
}
public interface Number{
int Num(String s);
}
private static void useNUmber(Number n){
int number = n.Num("666");
System.out.println(number);
}
}
控制台输出:666
Lambda被类方法替代的时候,它的形式参数全部传递给静态方法作为参数;
- 引用对象实例化方法;
public class UpperCaseDemo {
public static void main(String[] args){
//Lambda;
userPrint(s->System.out.println(s.toUpperCase()));
//引用对象; 对象::成员方法
PrintUpper Pu = new PrintUpper();
userPrint(Pu :: printUpper);
}
class PrintUpepr{
public void printUpper(String s){
String result = s.toUpperCase();
System.out.println(result);
}
}
interface Printer{
void PrintUpperCase(String s);
}
public static void usePrint(Printer p){
p.PrintUpperCase("Roger Federer")
}
}
控制台输出:ROGER FEDERER
Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数;
- 引用类的实例方法;
public class MyStringDemo {
public static void main(String[] args) {
//Lambda;
useMyString((s,x,y)->s.substring(x,y));
//引用类的实例方法; 类名::成员方法;
useMyString(String::substring);
}
public interface MyString{
String mySubString(String s , int x , int y);
}
private static void useMyString(MyString my){
String s = my.mySubString("Roger Federer", 0, 5);
System.out.println(s);
}
}
控制台输出:ROGER
Lambda表达式被类的实例化方法替代的时候;
第一个参数作为调用者;
后面的参数全部传递给该方法作为参数;
- 引用构造器方法;
public class Student {
String name;
int age;
public Student(){ }
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// Getter and Setter 方法省略
}
}
public class StudentDemo {
public static void main(String[] args) {
//Lambda;
useStudent((String name,int age)->{
Student s = new Student(name,age);
return s;
});
//调用构造器;类名 :: new
useStudent(Student :: new);
}
public interface StudentBuilder{
Student builder(String neme,int age);
}
public static void useStudent(StudentBuilder sb){
Student s = sb.builder("Chen",18);
System.out.println(s.getName() + "+" + s.getAge());
}
}
控制台输出:Chen+18
三、接口组成更新;
方法 | 格式 | 注意事项 |
---|---|---|
默认方法 | public default 返回值类型 方法名(参数列表) { } | * 默认方法不是抽象方法。所以不强制被实现类重写; * 如果需要重写的话需要去掉关键字default; * public 可以省略。 default 不可省略; |
静态方法 | public static 返回值类型 方法名(参数列表) { } | * 静态方法只能通过接口名调用,不可以通过实现类名或者对象名调用; * public 可省略 ; static不可省略 ; |
私有方法 | private 返回值类型 方法名(参数列表){ } pricate static 返回值类型 方法名(参数列表){ } | * 默认方法可以调用私有的静态方法和非静态方法; * 静态方法只能调用私有的静态方法; |
方法引用 | : : 就是引用符号 | * 如果使用Lambda,无需指定参数类型,无需指定重载形式,会被自动推导; * 如果使用引用方法,也是可以根据上下文进行推导; * 方法引用是Lambda的孪生兄弟; |
- 方法引用;
public class PrintableDemo {
public static void main(String[] args) {
//Lambda方法
usePrint( s->System.out.println(s));
//方法引用 -> :: 原则:可推导的就是可省略的;
usePrint(System.out::println);
//Lambda方法;
userPrintInt(p -> System.out.println(p));
//方法引用 ::
userPrintInt(System.out::println);
}
interface Printacble{
void printStrging(String s);
}
public static void usePrint(Printable p){
p.printStrging("爱学习");
}
public interface PrintInt {
void printInt(int i);
}
public static void userPrintInt(PrintInt printInt){
printInt.printInt(10);
}
}
1.使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法;
2.必须要有上下文环境,才能推导出Lambda对应的接口;
## 「局部变量的赋值」得知Lambda对应的接口: Runnable = () -> sout("Lambda表达式");
## 调用方法的参数」得知Lambda对应的接口: New Thread(()->sout("Lambda表达式")).start();