likes
comments
collection
share

什么是Lambda表达式?写一篇Lambda表达式的总结

作者站长头像
站长
· 阅读数 12

在Java中,当一个接口只有一个抽象方法时,则将其称之为函数式接口,为其提供一个Lambda表达式会让编译器为其生成一个实例对象,这个Lambda表达式中的代码块能覆盖这个实例对象的方法,并在以后执行。

什么是Lambda表达式,举例说明:

//这里有一个Dog接口
public interface Dog {
    void eat(String msg);
}

这里定义了一个Dog接口,由于这个接口中只有一个抽象方法,所以它是一个函数式接口,根据以往的经验如果想要使用这个接口中的eat方法,首先要创建一个实现了Dog接口的对象并重写eat方法。

Dog dog1 = new Dog() {
    @Override
    public void eat(String msg) {
        System.out.println(msg);
    }
};
dog1.eat("第一条小狗正在吃饭");

运行结果:第一条小狗正在吃饭

现在用Lambda表达式来代替上面这段代码:

//这是一个lambda表达式:(String msg) -> { System.out.println(msg);}
Dog dog2 = (String msg) -> { System.out.println(msg);};
dog2.eat("第二条小狗正在吃饭");

运行结果:第二条小狗正在吃饭

使用Lambda表达式只需要两行代码就能实现相同的功能,并且通过上面这段代码可以得到Lambda表达式的语法为: (params) -> { method body }

箭头左侧小括号内表示方法的类型及方法名,箭头右侧则为一个方法体,方法体中可以编写具体的代码。

方法引用:

在日常开发的过程中,是否经常见到使用以下这种方式打印某个字符串

String[] msg = "Hello world".split("");
Arrays.stream(msg).forEach(System.out::println);

这个表达式是一个方法引用System.out::println,System.out是PrintStream对象。这个表达式等价于(x) -> {System.out.println(x);},我认为可以把方法引用理解为Lambda的一种简写形式。与Lambda表达式一样,它会使编译器为函数式接口创建一个实例,并在实例中将抽象方法重写为给定的方法,在这里,System.out::println就是给定的方法。

需要注意的是,不管是Lambda表达式还是方法引用,本身都不是一个对象。只是当他们被传递到函数式接口时,这个接口就会被创建一个实例对象。

使用方法引用的几种形式:

  • 实例方法引用
    • 对象::实例方法 == System.out::println == (x) -> { System.out.println(x) }
    • 类名::实例方法 == String::toLowerCase == (x) -> { x.toLowerCase() }
  • 静态方法引用
    • 类名::静态方法 == Integer::sum == (x,y) -> { Integer::sum(x,y) }

对象::实例方法,示例代码:

//这里有一个Dog接口
public interface Dog {
    //吃东西
    void eat(String msg);
}
public class Test {
    public static void main(String[] args) {
        Dog dog = System.out::println;
        dog.eat("小狗吃东西了");
    }
}

运行结果:小狗吃东西了

类名::实例方法,示例代码:

//这里有一个Dog接口
public interface Dog {
    //叫两声
    String say(String msg);
}
public class Test {
    public static void main(String[] args) {
        Dog dog = String::toLowerCase;
        String msg = dog.say("Wang Wang Wang !!!");
        System.out.println(msg);
    }
}

运行结果:wang wang wang !!!

类名::静态方法,示例代码:

//这里有一个Dog接口
public interface Dog {
    //计算有几只小狗
    int count(int bdognum,int wdognum);
}
public class Test {
    public static void main(String[] args) {
        Dog dog = Integer::sum;
        int count = dog.count(6,8);
        System.out.println(count);
    }
}

运行结果:14

构造器引用

示例代码:

//这里有一个Dog接口
public interface Dog {
    //狗想要一个主人
    void withPeople(String m);
}
//这有个人
public class People {
    //人类构造器
    public People(String d){
        System.out.println(d);
    }

}
public class Test {
    public static void main(String[] args) {
        Dog d = People::new;
        d.withPeople("这个狗得到了一个主人");
    }
}

运行结果:这个狗得到了一个主人

其中,People::new就是一个构造器引用,Dog d = People::new;所做的操作实际上就是将函数式接口中的方法内容重写为new People(),由于构造方法中的参数是String类型,所以会自动调用public People(String d)构造器。当程序执行withPeople方法的时候就会去构建一个People实例。

关于Lambda表达式中变量的注意事项

下面这段代码会报错:

public class Test {
    public static void main(String[] args) {
        String a ="哈哈";
        Dog d = (x) -> {
            //注意,这一行会报错
            a = a+"";
            System.out.println(x);
        };
        d.say("wang");
    }
}

在idea编辑器中,a = a+"";会提示错误信息。原因是在Lambda中,不允许修改外围作用域中的变量值,因为如果可以在Lambda表达式中修改变量,多线程环境下就会不安全。

再看一个会报错的示例:

public class Test {
    public static void main(String[] args) {
        String a ="哈哈";
        a = a+"";
        Dog d = (x) -> {
            //这一行会提示错误信息,因为 a 在外部被修改了
            System.out.println(a);
        };
        d.say("wang");
    }
}

System.out.println(a);会报错,因为a在外部被修改了。由此可以判断,如果在表达式中引用了一个变量,那么这个变量即使是在表达式外部被改变也是不合法的。

其他注意事项:不能在lambda表达式中声明与外部变量同名的变量