java 正确的表达式_Java中如何正确的使用Lambda表达式

Java中如何正确的使用Lambda表达式

发布时间:2020-11-10 16:54:33

来源:亿速云

阅读:106

作者:Leah

这期内容当中小编将会给大家带来有关Java中如何正确的使用Lambda表达式,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

Lambda语法

Java中无法声明独立的纯函数,但是Lambda的出现提供了一种与独立函数更为近似的实现方式。就只看Lambda形式,的确与很多精简语法的脚本语言中所声明的函数高度相似:

# CoffeeScript

eat = (x) ->

alert("#{x} has been eatten!")

总之光看上去就像那么回事:)

那么Lambda表达式的语法又是怎样的呢?参数列表

Lambda体

两部分之间使用->分割,看几个例子:

p -> p.translate();

i -> new Point();

(a, b) -> return a + b;

() -> "Ha!";

(x, y, z) -> {

x += y;

y += z;

z += x;

}

箭头左边接收任意数量的参数,右边则为表达式体,描述所需的行为。

显而易见,在一般情况下无需显式地指定参数类型,除非上下文的信息无法是编译器推断出相应的类型:

(int x, int y) -> x + y;

参数可以声明为final,也可以添加注解(@Nullable, etc.)。

表达式体部分可以为方法的调用,如str.length()等等,也可以是表达式,如加减乘除等等,即“语句Lambda”与“表达式Lambda”这两种形式。

另外关于返回值,有则用return sth_to_return;,没有则用return;或直接不写返回语句。

最后,需要注意的是Lambda表达式不需要也不允许使用throws关键字来声明可能产生并需要向上抛出的异常。

Lambda与匿名内部类

前几篇文章中常常将Lambda与匿名内部类做粗浅的类比与对比,现在我们将就这一点做具体深入的分析。

语法

首先在语法层面,Lambda表达式有时候被称为匿名内部类的“语法糖”,这表明了二者之间存在语法繁简的明显区别。

无标识性问题

其次便是标识性问题,我们知道Java中为了区分对象,每一个对象(即使是匿名内部类的实例)都具有唯一标识,而依赖于对象而存在的行为(即我们所说的方法)也会与此标识相关联。

例如:

String bar = "bar";

String foo = "foo";

System.out.println(bar.hashCode()); // => 97299

System.out.println(foo.hashCode()); // => 101574

但是对于Lambda表达式而言,情况便不是如此的明朗,根据具体情况的不同,Lambda自身可能拥有标识也可能没有。

况且,Lambda为的就是表示一种行为,趋向于纯函数,因此一般情况下是不需要使用标识加以区分的。

作用域规则

再者就是两者的作用域大小的区别。

对于匿名内部类而言,显而易见,在类内可以沿用父类型(即函数接口)的名字。

而对于Lambda,则不能。

我们用Runnable接口来举一个例子:

public interface LetsRun extends Runnable {

String aString = "Big brother is watching.";

}

new Thread(

new LetsRun() {

@Override

public void run() {

System.out.println(aString);

}

}).run();

显然,匿名内部类能够直接沿用我们在LetsRun这个函数式接口中声明的aString。

写完这段代码的同时,IDE给了我一个可以将匿名内部类折叠为Lambda的提示,现在就让它帮我们自动折叠一下:

new Thread((LetsRun) () -> System.out.println(LetsRun.aString)).run();

注意此时需要打印的内容也同时自动变成了LetsRun.aString,印证了上述特征,即Lambda不能直接访问父类型中的名字。

关于对外部变量的访问(后面书中将此称为“变量捕获”),不论是匿名内部类还是Lambda,对于域外部变量的权限都是有限的。

在匿名内部类中,可以读取外部量,但是不允许有修改变量的倾向。

也就是说,没有严格的限制规定被访问的外部量必须被声明为final:

// This is OK

String anotherString = "WAR IS PEACE / FREEDOM IS SLAVERY / IGNORANCE IS STRENGTH";

// Also OK

final String finalString = "Nineteen Eighty-Four";

new Thread(new LetsRun() {

@Override

public void run() {

System.out.println(aString + "\n" + anotherString + "\n" + finalString);

}

}).run();

倘若一旦在方法内修改了anotherString的值,编译就无法通过。

同样,折叠为Lambda后,依然是合法的:

new Thread((LetsRun) ()

-> System.out.println(LetsRun.aString + "\n" + anotherString + "\n" + finalString)).run();

关于变量捕获的问题是下一小节的重点内容,在此暂时不做深究。

Lambda表达式在定义时,参数部分与表达式体内的命名可以暂时屏蔽掉字段的名称:

public class Foo {

String x, y;

BinaryOperator binaryOperator = (x, y) -> x.hashCode() + y.hashCode();

// ...

}

另外,Lambda相当于语句块,因此表达式体内持有和外部相同的语境,即this与super拥有相同含义:

public class MySuperClass {

public static final String aString = "Father";

}

public class MyClass extends MySuperClass {

public static final String aString = "Son";

public void aMethod() {

new Thread((LetsRun) () -> {

System.out.println("--- Lambda ---");

System.out.println(super.aString);

System.out.println(this.aString);

}).run();

System.out.println("--- Outside ---");

System.out.println(super.aString);

System.out.println(this.aString);

}

}

运行结果:

--- Lambda ---

Father

Son

--- Outside ---

Father

Son

Lambda无法引用自身,因此可以用一种尴尬的方式递归调用自己:

intUnaryOperator = i -> i == 0 ? 1 : i * intUnaryOperator.applyAsInt(i - 1);

小结

Lambda不从父类型中继承任何名字,包括:

接口的静态final字段

接口的静态嵌套类

默认方法(将在后续介绍)

将全部被排除在作用域之外。

Lambda参数与表达式体中的局部声明可以屏蔽字段名。

Lambda中的this和super的含义完全同外部一致。

而若在匿名内部类访问外部对象的当前实例须用OuterClass.this,非常笨拙:

new Thread((LetsRun) () ->

System.out.println(Foo.this.getClass().toString())

).run();

递归Lambda时须注意Lambda变量无法被初始化,只能直接调用相应函数式接口中的方法。

本章代码:

Foo.java

import java.util.function.BinaryOperator;

public class Foo {

String x, y;

BinaryOperator binaryOperator = (x, y) -> x.hashCode() + y.hashCode();

public static void main(String[] args) {

String bar = "bar";

String foo = "foo";

System.out.println(bar.hashCode());

System.out.println(foo.hashCode());

new Thread((LetsRun) () -> System.out.println(LetsRun.aString)).run();

String anotherString = "WAR IS PEACE / FREEDOM IS SLAVERY / IGNORANCE IS STRENGTH";

final String finalString = "Nineteen Eighty-Four";

new Thread((LetsRun) () -> System.out.println(LetsRun.aString + "\n" + anotherString + "\n" + finalString)).run();

new MyClass().aMethod();

new Foo().accessOuterClassInAnnoymousInnerClass();

}

public void accessOuterClassInAnnoymousInnerClass() {

new Thread((LetsRun) () ->

System.out.println(Foo.this.getClass().toString())

).run();

}

}

LetsRun.java

public interface LetsRun extends Runnable {

String aString = "Big brother is watching.";

}

MyClass.java

import java.util.function.IntUnaryOperator;

public class MyClass extends MySuperClass {

public static final String aString = "Son";

IntUnaryOperator intUnaryOperator = null;

public void aMethod() {

new Thread((LetsRun) () -> {

System.out.println("--- Lambda ---");

System.out.println(super.aString);

System.out.println(this.aString);

}).run();

System.out.println("--- Outside ---");

System.out.println(super.aString);

System.out.println(this.aString);

}

public void factorial() {

intUnaryOperator = i -> i == 0 ? 1 : i * intUnaryOperator.applyAsInt(i - 1);

}

}

MySuperClass.java

public class MySuperClass {

public static final String aString = "Father";

}

以及运行结果:

97299

101574

Big brother is watching.

Big brother is watching.

WAR IS PEACE / FREEDOM IS SLAVERY / IGNORANCE IS STRENGTH

Nineteen Eighty-Four

--- Lambda ---

Father

Son

--- Outside ---

Father

Son

class Foo

上述就是小编为大家分享的Java中如何正确的使用Lambda表达式了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值