2014年2月2日星期日

JAVA8初探之Lambda - 张珩

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
JAVA8初探之Lambda - 张珩  阅读原文»

临近JAVA8的发布,在这里分享一些JAVA8的试用体验。

先附上开发环境:

JDK8开发者预览版

IDEA13

JDK8API

Lambda

Lambda无疑是JAVA8最引人关注的改革,熟悉函数式语言的同学肯定已经体会到了它的便捷,先看个例子:

String runString = "run outside!";
new Thread(()->System.out.println("java8" + runString)).start();

new Thread(new Runnable() {
String runString = "run inside!";
@Override
public void run(){
System.out.println("java7" +this.runString);
}
}).start();

  上面两个线程分别打印了一句话,很简单的逻辑。第一种是lambda的实现,表达式中“()”代表无参输入,“->”可以理解为分隔符,后面是函数的实现。第二种实现方式层级嵌套较多,开发者关注的仅仅是run方法的实现,却要多写很多完全可以推断出来的代码。另外这里的作用域也有区别,lambda不允许方法实现里声明外部同名变量,将会编译不过。运行结果如下:

java8run outside!
java7run inside!

   在事件处理模块中经常会见到类似上面这种匿名内部类的代码,很多时候这类接口只有一个函数,在JAVA8里这种接口叫做函数式接口,函数式接口非常有价值的属性就是他们能够用lambdas来实例化,可以用@FunctionalInterface声明,当然不声明也不影响,前提是只有一个函数。看两个JDK8的接口源码:

@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();

public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;

   再看一个Callable的例子,主要是第二行初始化Callable集合:

ExecutorService executor = Executors.newFixedThreadPool(2);
List<Callable<String>> list = Arrays.asList((Callable) () -> "Hello",(Callable) () -> "world",(Callable) () -> "test");
List<Future<String>> futures;
try {
futures = executor.invokeAll(list);
for (Future future : futures) {
System.out.println((future).get());
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
executor.shutdown();

  

Stream

java.util.stream是JAVA8新增的包,的出现,方便了开发者像其他函数式语言一样简洁地操作集合,常见的如map,reduce,filter,foreach等函数。下面看个实现的例子,为了节省篇幅,只贴关键部分:

public class Person {
public enum Sex {
MALE, FEMALE
}
private String name;

private Integer age ;

private Sex gender;

public Person(String name, Integer age, Sex gender) {
this.name = name;
this.age = age;
this.gender = gender;
}

  

//初始化四个Person实例到一个list
List<Person> list = new ArrayList<>(
Arrays.asList(new Person("John",11,Person.Sex.MALE),
new Person("Robbin",22,Person.Sex.MALE),
new Person("Sarah",23,Person.Sex.FEMALE),
new Person("Amanda",23,Person.Sex.FEMALE))) ;
//将年龄大于12的Person名字和性别拼接起来再用“,”隔开,结果用“[”“]”括起来
System.out.println(list.parallelStream()
.filter(p -> p.getAge() > 12)
.map(p -> p.getName() + "_" + p.getGender())
.collect(Collectors.joining(",","[","]")));
//对性别为男的Person的年龄求和
System.out.println(list.stream()
.filter(p-> p.getGender() == Person.Sex.MALE)
.mapToInt(a->a.getAge()).sum());
//对所有实例的年龄求和
System.out.println(list.stream()
.map(p->p.getAge())
.reduce((x,y)->x+y).get());

   运行结果如下:

[Robbin_MALE,Sarah_FEMALE,Amanda_FEMALE]
33
79

   第一个用的是parallelStream,该函数是Stream针对多核处理器的并行版,有兴趣同学可以跟下源码看看实现。两个求和的区别在于前者是先生成int类型的映射集合再调用sum()求和,后者是用reduce累加的方式求和再用get()取值。有的函数依然返回Stream,如:filter,因此可以链式的调用,而有的函数没有返回Stream,如foreach和reduce,属与流的终端操作。再有就是Java中没有val和var的区分,使用中需要注意哪些函数是有状态的,哪些是无状态的。

Function

通过跟踪Stream的函数可以看到这些可接收lambda的参数类型都在java.util.function下。

Stream<T> filter(Predicate<? super T> predicate);

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

Optional<T> reduce(BinaryOperator<T> accumulator);

  

上面贴了三个Stream的函数,简单介绍下function包下几个主要的接口。

  • Function<T, R> -T作为输入,返回的R作为输出。

  • Predicate<T> -T作为输入,返回的boolean值作为输出。

  • Consumer<T> - T作为输入,执行某种动作但没有返回值。

  • Supplier<T> - 没有任何输入,返回T。

  • BinaryOperator<T> -两个T作为输入,返回一个T作为输出。

有了function的支持,我们就可以自己定义一些接口。

public static Integer compute(int value, IntFunction<Integer> function){
return function.apply(value);
}
public static Integer doubleOp(int value, Function<Integer, Integer> function1, Function<Integer, Integer> function2){
return function1.andThen(function2).apply(value);
}


public static void main(String[] args) {

int initNumber = 1;
IntFunction plus =val -> val +3;
System.out.println(compute(initNumber, plus));
System.out.println(doubleOp(3, val -> val + 2, val -> val + 3));

Function<Person,Integer> getAge = p -> p.getAge();
Function<Integer,Boolean> isAdult = a -> a>=18;
System.out.println(isAdult.compose(getAge).apply(new Person("test",19,Person.Sex.MALE)));


}

   运行结果:

4
8
true

  

如上面的Function例子所示,我们可以自己定义接收Function参数的方法,也可以用声明变量的方式直接给Function用lambda表达式赋值。andThen和compose是Function接口的两个方法,作用是组合两个Function实例,达到高阶函数的效果,区别在于andThen是调用者的函数先执行,compose是参数里的先执行。

文中有的地方用函数,有的地方用方法,写的时候也没多想。我个人的理解是,方法是基于实例的,是面向对象的;函数是不依赖实例的,是面向过程的,如果理解有误,还请斧正。

JAVA8的新特性还包括方法引用,接口的默认实现,以及其他的很多改进,后面有时间会补上其他的一些重要新特性。

JAVA8的很多改进对很多语言来说算不上创新,甚至在guava库里也能看到一些特性的影子,但作为主流语言还有这么大的改进,生命力还是很强的,值得期待。

14年3月JAVA8正式版就发布了,这里有JAVA8的时间表,文中例子用的是M8。虽然目前线上还以JAVA6为主,但是相信进步巨大的JAVA8会像当初JAVA5一样迅速成为主流版本。


本文链接:http://www.cnblogs.com/woodpecker/p/3537489.html,转载请注明。

上周热点回顾(1.27-2.2) - 博客园团队  阅读原文»

热点随笔:

· 学一点Git--20分钟git快速上手-Neil
· 软件开发真的这么简单吗?川山甲
· ASP.NET MVC 3升级至MVC 5.1的遭遇:“已添加了具有相同键的项”dudu
· 春节前最后一篇,CRUD码农专用福利:PDF.NET之SOD Version 5.1.0 开源发布(兼更名)深蓝医生
· 【管理心得之九】奉劝那些把组织“玩弄于鼓掌之间”的OL们。(别让组织看见此篇)小侯成长记
· 第一个工作年老黄.Hushy
· 基于ASP.NET MVC的热插拔模块式开发框架(OrchardNoCMS)--BootStrapNic Pei
· 回家前的挣扎——SQLite增删改查wolfy
· 去年做了什么?OA。Luo Indream
· 初入职场hmby2010
· 基于Json.NET自己实现MVC中的JsonValueProviderFactorydudu
· 13年的点滴与14年的盼头张小然

热点新闻:

· 趣图展现程序员职业生涯的11个阶段
· 腾讯未必生,阿里未必死
· 京东年度手机排行:第一名你绝对想不到
· 月球玉兔车,微博上一次灵性的进化
· 100年前人们眼中的2000年
· 10天后,腾讯移动支付第一!
· 斯诺登惊曝!外星人早已控制美国政府
· 创业 CEO:专注在餐盘,而不是楼盘
· 大四男生设计双面U盘 拿下设计界奥斯卡!
· 马云:微信红包有如“珍珠港偷袭”
· 一晚不关4G 房子归移动?
· 吃货逆天了 网上卖吃相月入近万美元

知识库热点文章:

· 全栈工程师就是一棵歪脖子树
· 浏览器中关于事件的那点事儿


本文链接:http://www.cnblogs.com/cmt/p/3537470.html,转载请注明。

阅读更多内容

没有评论:

发表评论