我的学习笔记——Lambda表达式

我的学习笔记——Lambda表达式

  • 1.什么是Lambda表达式
  • 2.为什么要使用Lambda表达式
  • 3.Lambda表达式的写法
  • 4.Lambda表达式的变量作用域

1.什么是Lambda表达式

Lambda表达式在菜鸟教程中被这样描述:

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

Lambda表达式的写法为:

( 参数列表... ) -> {  方法体 };

中间的 “ ->” 可以被读作“ goes to ”
比如要传入参数a和b,返回a与5的和,可以写成:

(a) ->{ return a + 5 ;};

可读作 a gose to a + 5,
我认为Lambda表达式看起来更像一个匿名函数


2.为什么要使用Lambda表达式

Lambda表达式被用来更简洁的实现接口,我们知道实现接口有三种基本方式:

  • 定义实现类实现接口
  • 匿名内部类实现接口
  • Lamdba表达式实现接口

假如现有一个USB接口

public interface USB {
    public void connect();
}

有键盘,硬盘,手机三个类对USB接口进行不同实现
1)用实现类实现接口

class Keyboard implements USB{
    @Override
    public void connect() {
        System.out.println("连接键盘,进行输入");
    }
}

public class Test{
    public static void main(String[] args) {
        Keyboard keyboard = new Keyboard();
        keyboard.connect();
    }
}

2)匿名内部类实现接口

public class Test{
    public static void main(String[] args) {
    	//匿名内部类实现接口
        USB hardDisk = new USB() {
            @Override
            public void connect() {
                System.out.println("连接硬盘,传输数据");
            }
        };
        hardDisk.connect();
    }
}

3)Lambda表达式实现接口

public class Test{
    public static void main(String[] args) {
        //Lambda表达式实现接口
        USB phone = () -> {
            System.out.println("连接手机,给手机充电");
        };
        phone.connect();
    }
}

我们可以看出Lambda表达式实现接口的方式简洁明了,吊打前两种实现方式,但是Lambda只能实现有且仅有一个抽象方法的接口,这样的接口也叫做函数式接口,函数式接口可以被隐式转换为 Lambda 表达式,而且Lambda表达式中没有体现接口中抽象方法的名字,所以我认为Lambda表达式更像一个匿名函数。定义函数时接口时可以在接口前加上@FunctionalInterface注解。


3.Lambda表达式的写法

Lambda表达式的语法为

接口 引用名 = (参数列表...) -> { 方法体;};

假设现在我们有4个函数式接口,分别包含无参数无返回值方法,无参数有返回值方法,有参数无返回值方法,有参数有返回值方法

1)无参数无返回值方法

@FunctionalInterface
public interface NoParameterNoReturnedValue {
    public abstract void method();
}

2)无参数有返回值方法

@FunctionalInterface
public interface NoParameterOneReturnedValue {
    public abstract int method();
}

3)有参数无返回值方法

@FunctionalInterface
public interface OneParameterNoReturnedValue {
    public abstract void method(String str);
}

4)有参数有返回值方法

@FunctionalInterface
public interface OneParameterOneReturnedValue {
    public abstract int method(int num);
}

用Lambda表达式实现接口

public class LambdaTest {
    public static void main(String[] args) {
        //无参无返回
        NoParameterNoReturnedValue   nn = () -> { 
            System.out.println("hello world!"); 
        };
        //无参返回int 1-10 随机数
        NoParameterOneReturnedValue  no = () -> {
            return (int) (Math.random()*10); 
        };
        //有String类参数无返回值 拼接字符串在控制台输出
        OneParameterNoReturnedValue  on = (String name) -> {
            System.out.println("Hi " + name + "!"); 
        };
        //有int参数,返回int值加10
        OneParameterOneReturnedValue oo = (int num) -> { 
            return num + 10; 
        };

        nn.method();
        System.out.println( no.method() );
        on.method("Jack");
        System.out.println( oo.method(5) );
    }
}
//output:
//    hello world!
//    3
//    Hi Jack!
//    15

而实际上,我们可以对Lambda表达式进行更简略地描写:
1)如果方法体里只有一个语句,我们可以省略包围方法体的花括号,如果这一个语句恰好是返回一个表达式的return语句,我们甚至可以省略return关键字
2)在参数列表中,我们可以不显式地声明参数类型,编译器可以统一识别参数值。假如接口中的抽象方法只有一个参数,那么我们也可以省略包围参数列表的圆括号
对于上面的有参有返回值接口的实现,我们可以这样写:

OneParameterOneReturnedValue oo = num -> num + 10;

4.Lambda表达式的变量作用域

Lambda表达式似乎可以引用外围的变量

public class LambdaTest {
    public static void main(String[] args) {
        int num = 10 ;
        OneParameterOneReturnedValue oo = myNum -> num + myNum;
        System.out.println(oo.method(5));
    }
}//output:
//15

在这里,在Lambda表达式中可以引用外围的变量num,并返回了num值与我输入的myNum值的和,但是可能经过Lambda表达式引用过的变量就被修饰为final变量了,当我无论在Lambda表达式中修改引用的num值或者在Lambda表达式之后修改num的值,编译器都会报这样的错误:

int num = 10 ;
        OneParameterOneReturnedValue oo = myNum -> {
            num ++;//报错
            return num + myNum;
        };
        System.out.println(oo.method(5));
int num = 10 ;
        OneParameterOneReturnedValue oo = myNum -> num + myNum;
        System.out.println(oo.method(5));
        num += 10 ;//报错

报错:

Variable used in lambda expression should be final or effectively final

我也不是很明白其中的道理


对于使用Runnable接口开辟一个新线程的时候,我们可以使用Lambda表达式实现Runnable接口:

Runnable runnable = () -> {
            int countdown = 10;
            while ( countdown-- >0)
                System.out.println(countdown>0 ? "倒计时"+ countdown +"秒" : "发射!");
        };
new Thread(runnable).start();

或者直接:

 new Thread(() -> {
            int countdown = 10;
            while ( countdown-- >0)
                System.out.println(
                        countdown>0 ? "倒计时"+ countdown +"秒" : "发射!"
                );
        }).start();

这大概就是在开头所提到的

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

这样就相当于直接把Runnable的具体实现传入Thread的构造器中了。

你可能感兴趣的:(我的学习笔记——Lambda表达式)