lambox java_Java世界里的Lambda

作为Java世界的大事件Java 8终于在2014年3月18日发布了。在Java 8中,最令人期待的新特性,就属Lambda表达式的支持。其实Lambda在一些脚本语言(如Python,Ruby)中早已存在,但是对于Java程序员来说,这还是新鲜事。Lambda是函数式编程的基础。对习惯Java语法的朋友们来说,理解Lambda有点小困难,至少我是花了不少时间才搞懂的。当然,如果你是从脚本语言开始学习的,估计就不成问题了。本文的主要目的,就是帮助Java程序员,理解Lambda表达式。

Java中多线程的实现,我假设大家都知道。一般的做法是创建一个类去实现Runnable接口,实现其中的run方法。再通过这个类的对象创建一个线程对象。然后再调用这个对象的start()函数。

public class ThreadTest implements Runnable {

public void run() {

System.out.println("I'm running!");

}

}

...

ThreadTest tt = new ThreadTest();

Thread t = new Thread(tt);

t.start();

这样写很繁琐,所以大部分程序员都会用匿名内部类。

Thread t = new Thread(new Runnable {

public void run() {

System.out.println("I'm running!");

}

});

t.start();

上面的代码看上去简洁不少,但还是有些晦涩。当Lambda出现后,你所要做的就是一行代码。

Thread t = new Thread(() -> System.out.println("I'm running!"));

t.start();

让我们看看发生了什么。对于只有一个显式声明的抽象方法的接口,Java 8引入了一个新的概念,叫”函数接口(functional interface)“。如果你自己创建,建议用@FunctionalInterface标注出来,当然不标也不会报错。而Lambda表达式的作用,就是可以为这个函数接口赋值,同样来说,也就可以替代匿名内部类的作用。如上面线程的例子,你就可以用下面的表达式为Runnable接口赋值。

Runnable r = () -> System.out.println("I'm running!");

详细分析下等号右边这段表达式,把二元操作符->两边的内容分别来看

左边() 等同于函数接口中唯一的那个抽象函数的参数。因为Runnable中run()函数没有参数,所以就写成()。该函数有多少参数,就写多少,比如(x, y, z) -> xxx

右边System.out.println("I'm running!");就是那个抽象函数的实现。这个部分,可以包含多条语句,需要用{}括起来,并用分号分隔。如

button.addActionListener(e -> {

ui.dazzle(e.getModifiers());

ui.showSomething();

});

注意,这边建议即使只有一条语句,也要加上{}。就像Java代码规范里,if语句后面一样。这样做,是为了避免将来代码改变可能带来的隐患。

如果需实现的抽象函数有返回值,那么右边最后一条语句,也必须返回同样类型的返回值。如果右边只有一条语句,那return关键字就可以省去

File dir = new File("/home/user/test");

File[] files = dir.listFiles((File f) -> {f.isFile();});

留个问题给大家,这里的参数f前面为什么要加File类型声明?(当然,不加也能跑,不过强烈建议加上)

到这里,大家是不是已经领会Lambda表达式的使用了?那么有人不禁要问,这个Lambda除了写起来简洁一些,它到底有什么作用呢?这边说下我的理解:

减少无意义的类的创建

如果你有一个Animal接口如下,里面包括一个”吼叫”的函数

public interface Animal {

public void roar() ;

}

你要创建几种不同的小动物,那你就要创建好几个类

public class Cat implements Animal {

public void roar() {

System.out.println("Miu Miu!");

}

}

public class Dog implements Animal {

public void roar() {

System.out.println("Wow Wow!");

}

}

public class Cow implements Animal {

public void roar() {

System.out.println("Moo Moo!");

}

}

Cat cat = new Cat();

Dog dog = new Dog();

Cow cow = new Cow();

这看上去多麻烦啊。让我们用Lambda试试:

Animal cat = () -> System.out.println("Miu Miu!");

Animal dog = () -> System.out.println("Wow Wow!");

Animal cow = () -> System.out.println("Moo Moo!");

立马代码简化了好多。如果这些类不常用,使用Lambda会省不少事。

传递操作,而不只是传递数据

有点类似于设计模式中的策略模式,我把程序的流程写好,但是中间核心逻辑留给别人来实现。

public interface NumberValidation {

public boolean validate(int num) ;

}

public int sum(List numbers, NumberValidation v) {

int total = 0;

// 对集合中的数求和

for (int number: numbers) {

// 过滤数字,但此时并不知道过滤的逻辑是什么

if (v.validate(number)) {

total += number;

}

}

return total;

}

所以我们就可以用下面方法对不同情况求和: 求奇数和

sum(numbers, n -> n % 2 == 1);

求偶数和

sum(numbers, n -> n % 2 == 0);

求小于10的数的和

sum(numbers, n -> n < 10);

流式操作

Java 8为集合类引入了stream(流)的概念。一个流以集合的实例作为输入数据源,然后像管道一样从一个操作流到另一个操作。每个操作返回另一个流,并作为下个操作的输入。每一步操作都非常原子,通常用一个Lambda表达式来实现。大家看看下面的例子:

List numbers = xxx; // 这里假设创建一个整数集合从1到10

numbers.stream() // 将集合变成一个流,也就是集合里的数据会一条一条从这里流出

.filter(n -> n % 2 == 0) // 对每条数据过滤,由Lambda表达式实现,这里是取偶数

.map(n -> n * n) // 对每条数据做映射,这里是将原来的数据映射成其平方值

.forEach(n -> System.out.println(n)); // 对每条数据做打印。

在这个例子中,集合类对象numbers作为数据源,通过stream()方法生成一个流;filter()方法过滤出偶数,然后返回新的流,并作为map()方法的输入;map()方法将每个数映射到它的平方,然后返回另一个新的流,并作为forEach()方法的输入;最后由forEach()方法将结果打印出来。forEach()不再返回流了。上面这段程序的最后输出就是:

4

16

36

64

100

这是一个典型的函数式编程的例子。函数式编程是一种思想,这篇我就不多解释了,感兴趣的朋友们可以学习下Groovy或Scala语言来深入了解其中的奥秘。

到这里,我们介绍了Lambda表达式在Java中的使用,及其价值,大家是不是觉得挺有趣的呢?当然使用Lambda表达式还有很多好处,里面的知识点也远不止我写的这些。这里就不一一赘述了。现在Lambda相关的参考资料很多,想深入学习的话,大家可以多在网上找找。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值