代码手工艺人

Joey 写字的地方

最近在 OSChina 上翻译版块有一个系列(共 4 篇)关于 Guava/Google Collections 库的文章,我也有幸翻译了一部分。Guava 的中文意思是番石榴,这个库的功能和名字一样诱人,很好很强大,使用起来也很方便,强烈推荐。
这几篇文章都是 2009 年写的,现在的 Guava 库应该已经更新了很多,不过对于了解 Guava 库还是 OK 的。

Guava 托管在 Google Code 上的地址在这里,目前最新版本是 14.0

最初的 Blog 放在了 GAE 上,使用一个叫 B3log 的 Blog 程序,但是间歇性的被墙访问不了,或者就是打开页面很慢,用着纠结;然后打算自己搞个 VPS,于是乎在 42qu 上买了个 VPS,搭建了一个 WordPress,使用很方便,但是需要经常担心服务器哪天挂掉,或者被攻击,或者哪个模块出问题要跑到服务器上检查。而且 WordPress 的绝大多数功能我也用不着,我一般就是写点文字放点代码而已,再加上 Jekyll 也很热,就尝试了一下 Jekyll,发现这才是我想要的 Blog 形式,非常简单,页面也很简洁,写作方式也很 Geek。打算暂时用 Jekyll 来写东西。

Java8 带有 Lambda 表达式的预览版的 JDK 已经放出来了(地址在最下面),新特性有以下四个:

  1. Lambda 表达式(或称之为 “闭包” 或者 “匿名函数”)

  2. 扩展的目标类型

  3. 方法和构造器引用

  4. 接口默认方法

本文先介绍一下 Java8 中很值得期待的 Lambda 表达式,Lambda 表达式,等同于大多说动态语言中常见的闭包、匿名函数的概念。其实这个概念并不是多么新鲜的技术,在 C 语言中的概念类似于一个函数指针,这个指针可以作为一个参数传递到另外一个函数中。由于 Java 是相对较为面向对象的语言,一个 Java 对象中可以包含属性和方法(函数),方法(函数)不能孤立于对象单独存在。这样就产生了一个问题,有时候需要把一个方法(函数)作为参数传到另外一个方法中的时候(比如回调功能),就需要创建一个包含这个方法的接口,传递的时候传递这个接口的实现类,一般是用匿名内部类的方式来。如下面代码,首先创建一个 Runnable 的接口,在构造 Thread 时,创建一个 Runnable 的匿名内部类作为参数:

1
2
3
4
5
new Thread(new Runnable() {  
public void run() {
System.out.println("hello");
}
}).start();

类似这种情况的还有 swing 中 button 等控件的监听器,如下面代码所示,创建该接口的一个匿名内部类实例作为参数传递到 button 的 addActionListener 方法中。

1
2
3
4
5
6
7
8
9
public interface ActionListener {   
void actionPerformed(ActionEvent e);
}

button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.dazzle(e.getModifiers());
}
});

这样的代码的缺点是有代码笨重,可读性差,不能引用外面的非 final 的变量等。lambda 表达式就是为了解决这类问题而诞生的。

在介绍 Java8 中的 Lambda 表达式之前,首先介绍一个概念叫 “函数式接口”(functional interfaces)。对于任意一个 Java 接口,如果接口中只定义了唯一一个方法,那么这个接口就称之为 “函数式接口”。比如 JDK 中的 ActionListener、Runnable、Comparator 等接口。

先来看一下 Java8 中的 lambda 表达式的使用示例:

创建一个线程:

1
new Thread(() -> {System.out.println("hello");}).start();

可以看到这段代码比上面创建线程的代码精简了很多,也有很好的可读性。
() -> {System.out.println(“hello”);} 就是传说中的 lambda 表达式,等同于上面的 new Runnable(),lambda 大体分为 3 部分:

1. 最前面的部分是一对括号,里面是参数,这里无参数,就是一对空括号

2. 中间的是 -> ,用来分割参数和 body 部分

3. 是 body 部分,可以是一个表达式或者一个语句块。如果是一个表达式,表达式的值会被作为返回值返回;如果是语句块,需要用 return 语句指定返回值。如下面这个 lambda 表达式接受一个整形的参数 a,返回 a 的平方

1
2
3
(int a) -> a^2   
//等同于
(int a) -> {return a^2;}

继续看更多的例子:

1
2
3
4
5
(int x, int y) -> x + y  

() -> 42

(String s) -> { System.out.println(s); }

创建一个 FileFilter,文件过滤器:

1
FileFilter java = (File f) -> f.getName().endsWith(".java")

创建一个线程:

1
2
3
new Thread(() -> {  
//do sth here...
}).start()

创建一个 Callable:

1
Callable<String> c = () -> "done";

而且 lambda 表达式可以赋值给一个变量:

1
2
Comparator<String> c;  
c = (String s1, String s2) -> s1.compareToIgnoreCase(s2);

当然还可以作为方法的返回值:

1
2
3
4
5
public Runnable toDoLater() {  
return () -> {
System.out.println("later");
};
}

从上面可以看到,一个 lambda 表达式被作为一个接口类型对待,具体对应哪个接口,编译器会根据上下文环境推断出来,如下面的 lambda 表达式就表示一个 ActionListener.

1
ActionListener l = (ActionEvent e) -> ui.dazzle(e.getModifiers());

这有可能会造成一个表达式在不同的上下文中被作为不同的类型,如下面的这种情况,尽管两个表达式是相同的,上面的表达式被推断为 Callable 的类型,下面的会被推断为 PrivilegedAction 类型。

1
2
Callable<String> c = () -> "done";  
PrivilegedAction<String> a = () -> "done";

那么编译器是根据哪些因为决定一个表达式的类型呢?

如果一个表达式被推断为是 T 类型的,需要满足以下 4 个条件:

  1. T 是函数式接口类型(只声明唯一一个方法)
  2. 表达式和 T 中声明的方法的参数个数一致,参数类型也一致
  3. 表达式和 T 中声明的方法的返回值类型一致
  4. 表达式和 T 中声明的方法抛出的异常一致
    有了这个准则,上面的疑问就迎刃而解了

参考:

1.State of the Lambda

2.Java8 带有 Lambda 表达式版本的 JDK 下载地址

作者:Dustin Marx 发表日期:Fri, 03/02/2012 - 23:10.

Reddit Java 网站最近有一个题目为 “分享 Java 标准类库中一些有用的类” 的讨论话题,注解栏为 “有很多平常我们没有认识到的类,分享一些你经常使用而我们可能没有意识到的类吧!”。在这篇文章中,我看到下面回复超过 40 的一些类(大部分是 JDK 中的)。

有一些回复者分享的是与并发相关的 Java 类,如 Executors, java.util.concurrent.CountDownLatch, java.util.concurrent.atomic.AtomicInteger, ThreadLocal, java.util.concurrent 以及包下的所有类以及 java.util.concurrent.atomic.

还有一些与 String 处理相关的类也被提到了,包括 StringBuffer, 和 StringBuilder. 我在博文 String, StringBuffer, and StringBuilder: There Is A Performance Difference 中也提到了这些与 String 相关的类。其他与 String 相关并被提到的包括 java.util.StringTokenizerApache Commons‘ StringUtils (在我的文章 Checking for Null or Empty or White Space Only String in Java 中也有提到). java.util.Scanner 类也可以让简化文本解析。

在用户界面上, java.awt.geom 包, java.awt.Desktop 以及 javax.swing.SwingUtilities 被提到。 java.awt.Point 被高亮显示,原因总结为:” 任何两个 int 值对可以很简单的被用来代替数组传递给函数,或者从函数中返回,都可以使用 Point 类”。java.awt.Robot 类也在文中被提到,我之前也在我的一篇文章 Screen Snapshots with Java’s Robot 中提到。

不出所料,一些 Java 集合类也在列在其中。包括 java.util.EnumSetEnumMap (参考我的文章 The Sleek EnumMap and EnumSet), java.util.ArrayDeque (参考我的文章 The Java SE 6 Deque), java.util.PriorityQueue,java.util.Arrays, 以及 java.util.Collections (参考我的文章 The Java Collections Class).

以我之见,java.lang.ClassLoader, java.util.ServiceLoaderjava.nio.file.FileVisitor 是 Reddit Java 话题中提到的更精心构思和特别的类。我们大部分的情况下都是用强引用(strong reference),但 java.lang.ref.WeakReference (弱引用)和 java.lang.ref.SoftReference (软引用)也在讨论中被提到。

最后,我经常使用的一个类和一个方法分别是 BigDecimal 类(我的一边文章中顺便提到了该类: Caution: Double to BigDecimal in Java )和 System.nanoTime()

结论:

我喜欢列表中的很多类,也能想到一些其他的例子。特别的,我想 JDK7 中的 Objects 类也能称得上很有用但鲜为人知的一个类把。我同样同意其中一个评论的说法:“把 Google guava 库添加进来吧!”,我也写过一些博文关于 Guava

原文:http://marxsoftware.blogspot.com/

翻译自:http://www.javaworld.com/community/node/8335

0%