Hello World
Spiga

浅谈Java 7的闭包与Lambda表达式之优劣

2010-06-06 15:31 by 老赵, 16648 visits

前几天Oracle推出了Java 7官方的闭包与Lambda表达式的第一个实现,这基本上也是最终在正式版中的样式了。看了这个实现之后,我的第一感觉便是“丑”,当然不排除这是因为看惯了其他语言中实现的缘故。后来再仔细看了看又想了想,发现Java 7的实现也并非毫无可取之处,但似乎又感到某些做法上有一些问题。总之整个过程颇为有趣,决定将我的想法记录下来,希望可以吸引人来一起讨论一下。

Java 7中的Lambda表达式

Java 7中的Lambda表达式有两种形式,首先是第一种:

#int() func1 = #()(3); // "func1.()" returns 3
#int(int) func2 = #(int x)(x + 1); // "func2.(3)" returns 4
#int(int, int) func3 = #(int x, int y)(x - y); // "func3.(5, 3)" returns 2 

然后是第二种,含义与上面等价:

#int() func1 = #(){ return 3; };
#int(int) func2 = #(int x){ return x + 1; };
#int(int, int) func3 = #(int x, int y){ return x – y; };

如果Lambda的body是“单个表达式”的话,便可以使用“小括号”,并省去最后的return关键字;如果body中需要包含多条语句的话,则必须使用“大括号”,而大括号内部可以包含多条语句,就像一个普通的方法体一样。这两种写法在C#中也有对应物,如在“单个表达式”的情况下:

// C#
Func<int> func1 = () => 3; // "func1()" returns 3
Func<int, int> func2 = x => x + 1; // "func2(3)" returns 4 
Func<int, int, int> func3 = (x, y) => x - y; // "func3(5, 3)" returns 2

第二种,即多条语句:

// C#
Func<int> func1 = () => { return 3; };
Func<int, int> func2 = x => { return x + 1; };
Func<int, int, int> func3 = (x, y) => { return x – y; };

Java和C#的Lambda表达式都由两部分组成:“参数列表”和“表达式体”,但是它们有如下区别:

  • 在Java中参数列表和表达式体之间没有分隔符号,而C#使用“=>”分隔。
  • 对于“单个表达式”的Lambda来说,C#可以无需使用括号包含表达式体,而Java必须使用小括号。
  • 如果只有单个参数,那么C#的参数列表可以省去小括号,而Java必须保留。
  • C#对参数列表会进行“类型推断”,而Java必须写清参数类型。

这些区别说大可大,说小可小,但是Java语言的设计的确让我感觉较C#为“丑”,这可能是个人主观因素,但我认为也不尽然。例如,如果我们需要对一个用户对象数组按照“年龄”进行排序,在C#里可以写作:

// C#
users.Sort(u => u.Age);

而在Java中则必须写为:

Arrays.sort(users, #(User u)(u.Age));

这句C#代码语义清晰:按照“u的Age进行排序”,而在Java代码中便显得比较累赘,语义似乎也不够清晰。Anders在设计C#语法的时候非常注重“声明式”代码,由此可见一斑。此外,我不明白为什么Java选择不对参数进行类型推断,在我看来这对于写出优雅代码十分重要(关于这点,在“Why Java Sucks and C# Rocks”系列中会有更详细的讨论)。不过Java也不是没有“推断”,例如从上面的代码片断中可以得知,Java对于Lambda表达式的返回值还是进行了类型推断。事实上,Java还推断了“异常类型”,这点稍后会有更多讨论。

当然,Java中可以“无中生有”地定义“匿名函数类型”(这点和VB.NET相对更为接近),而不需要像C#一样需要基于特定的“委托类型”,显得更为灵活。

SAM类型支持及闭包

SAM的全称是Single Abstract Method,如果一个类型为SAM类型,则意味着它 1) 是抽象类型(即接口或抽象类),且 2) 只有一个未实现的方法。例如这样一个Java接口便是个SAM类型:

public interface Func<T, R> {
    R invoke(T arg);
}

于是我们便可以:

Func<int, int>[] array = new Func<int, int>[10];
for (int i = 0; i < array.length; i++) {
    final int temp = i;
    array[i] = #(int x)(x + temp);
}

可见,我们使用Lambda表达式创建了Func接口的实例,这点是C#所不具备的。这点十分关键,因为在Java类库中已经有相当多的代码使用了SAM类型。不过我发现,在某些使用SAM的方式下似乎会产生一些“歧义”,例如这段代码:

public class MyClass {
    @Override
    public int hashCode() {
        throw new RuntimeException();
    }

    public void MyMethod() {
        Func<int, int> func = #(int x)(x * hashCode());
        int r = func.invoke(5); // throw or not?
    }
}

在这里我们覆盖(override)了MyClass的hashCode方法,使它抛出RuntimeException,那么在调用MyMethod中定义的func1对象时会不会抛出异常?答案是否定的,因为在这个Lambda表达式中,隐藏的“this引用”代表了func对象,调用它的hashCode不会抛出RuntimeException。那么,假如我们要调用MyClass的hashCode怎么办?那就稍微有些麻烦了:

Func<int, int> func = #(int x)(x * MyClass.this.hashCode());

不过从另一段示例代码上看:

public class MyClass {

    public int n = 3;

    public void MyMethod() {
        Func<int, int> func = #(int x)(x + n);
        int r = func.invoke(5); // 8
    }
}

由于Func对象上没有n,因此这里的n便是MyClass类里定义的n成员了。因此,Java的闭包并非不会捕获字面上下文里的成员,只是在SAM类型的情况下,字面范围内(lexical scope)成员的优先级会低于目标抽象类型的成员。

总体来说,对于SAM类型的支持上,我认为Java是有可取之处的,只是我始终认为这个做法会产生歧义,因为我印象中其他语言里的Lambda表达式似乎都是捕获字面上下文的(当然它们可能也没有SAM支持)。但是,如何在“歧义”和“优雅”之间做出平衡,我一时也找不到令人满意的答案。

硬伤:Checked Exception

Java相当于其他常见语言有一个特别之处,那就是Checked Exception。Checked Exception意味着每个方法要标明自己会抛出哪些异常类型(RuntimeException及其子类除外),这也是方法契约的一部分,编译器会强制程序员写出满足异常契约的代码。例如某个类库中定义了这样一个方法:

public void myMethod() throws AException, BException

其中throws后面标注的便是myMethod可能会抛出的异常。于是如果我们要写一个方法去调用myMethod,则可能是:

public void myMethodCaller() throws AException {
    try {
        myMethod();
    } catch (BException ex) {
        throw new AException(ex);
    }
}

当我们写一个方法A去调用方法B时,我们要么在方法A中使用try...catch捕获B抛出的方法,要么在方法A的签名中标记“会抛出同样的异常”。如上面的myMethodCaller方法,便在内部处理了BException异常,而只会对外抛出AException。Java便使用这种方法严格限制了类库的异常信息。

Checked Exception是一个有争议的特性。它对于编写出高质量的代码非常重要,因为在哪些情况抛出异常其实都是方法契约的一部分(不仅仅是签名或返回值的问题),应该严格遵守,在类库升级时也不能破坏,否则便会产生兼容性的问题。例如,您关注MSDN里的文档时,就会看到异常的描述信息,只不过这是靠“文档”记录的,而Java则是强制在代码中的;但是,从另一个角度说,Checked Exception让代码编写变得非常麻烦,这导致的一个情况便是许多人在写代码时,自定义的异常全都是RuntimeException(因为不需要标记),每个方法也都是throws Exception的(这样代码中就不需要try...catch了),此时Checked Exception特性也基本形同虚设,除了造成麻烦以外几乎没有带来任何好处。

我之前常说:一个特性如果要被人广泛接受,那它一定要足够好用。现在如Scala和Grovvy等为Java设计的语言中都放弃了Checked Exception,这也算是从侧面印证了Checked Exception的尴尬境地吧。

而Checked Exception对于如今Lambda或闭包来说,在我看来更像是一种硬伤。为什么这么说?举个例子吧,假如有这么一个map方法,可以把一个数组映射成另一个类型数组:

public R[] map(T[] array, Func<T, R> mapper) { ... }

好,那么比如这样一个需求:给定一个字符串数组,保存着文件名,要求获得它的标准路径。从表面上看来,我们可以这样写:

map(files, #(String f)(new File(f).getCanonicalPath())

但事实上,这么做无法编译通过。为什么?因为getCanonicalPath方法会抛出IOException,我们在调用时必须显式地使用try...catch进行处理。那么这段代码该怎么写?还真没法写。如果没有Checked Exception的话(如C#),我们还可以这么做(处理第一个抛出的IOException):

try {
    map(files, #(String f)(new File(f).getCanonicalPath())
catch (IOException ex) {
    ...
}

但是,如果我们要写出之前那种“漂亮”的写法,就不能使用Func<T, R>而必须是这样的接口类型:

public interface FuncThrowsIOException<T, R> {
    R invoke(T arg) throws IOException;
}

或者是这样的“匿名函数类型”:

#String(String)(throws IOException) // toCanonicalPath = #(String f)(new File(f).getCanonicalPath())

但是,作为Lambda和闭包的常用场景,如map,filter,fold等“函数式”元素,是不可能为某种特定的“异常类型”而设计的——异常类型千变万化,难道这也要用throws Exception来进行“统一处理”吗?Java虽然已经支持对异常类型的“推断”,但Checked Exception还是对Lambda和闭包的适用性造成了很大影响。

因此,我认为Checked Exception是一个“硬伤”。

其他

Java的Lambda和闭包还有一些特性,例如参数的“泛化”:

#boolean(Integer) f = #(Number n)(n.intValue() > 0);

由于Number是Integer的基类,因此我们可以使用Number来构造一个接受Integer参数的匿名函数类型。由于示例较少,我还不清楚这个特性的具体使用场景和意义所在——不过我猜想,在Java中可能允许这样做吧:

#boolean(Number) f = #(Number n)(n.intValue() > 0);
#boolean(Integer) f1 = f; // cast implicitly or explicitly

此外还有一些特性,例如与MethodHandle类型的转化,我就没有特别的看法了。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

62 条回复

  1. xiaowen
    123.152.43.*
    链接

    xiaowen 2010-06-06 16:39:39

    老赵讲Java的一些列文章,似乎更印证了Anders接受采访时说的话:

    说到语法, C#和Java都是C和C++家族中的成员. 当然, 你可以说C#的代码看起来像Java代码, 但你也可以说Java代码看起来像C代码. ...所以, 谁碍着谁了呢 我不觉得谁模仿了谁. 编程的语言进化起来比硬件慢多了; 它们缓慢的前进, 而我们都是站在巨人的肩膀上. Java从C和C++继承了很多, 而同时Java也给了我们很多灵感.

    C# 确实是站在 Java 这个巨人的肩膀上。

  2. 老赵
    admin
    链接

    老赵 2010-06-06 18:04:39

    @xiaowen

    你怎么看出这种感觉的啊?我觉得你明显没有把握住中心思想嘛。这话五年前说说还差不多,现在C#早就把Java甩开N条马路了。如果说C# 1.0受到Java不少影响的话,那么从C# 2.0开始的各种“灵感”就没有Java的份了。

    现在还强调这句话,就好比有人说“爱因斯坦小学时还抄过我作业呢”,说错么……真没错,但听上去总是很可笑的,呵呵。

  3. xiaowen
    123.152.43.*
    链接

    xiaowen 2010-06-06 19:34:23

    汗... 你的比喻也太夸张了

    我觉得,正是因为 C#1.0 的时候看到了 Checked Exception 的问题,所以才避免了今天你文章中提到的尴尬...

    然后 C#3.0 的时候增加了扩展方法,然后 Java 想,你小时候还抄袭我的呢,要我抄袭你长大后的东西,我才不干呢...然后才有

    // C# 
    users.Sort(u => u.Age);
    

    而在Java中则必须写为:

    Arrays.sort(users, #(User u)(u.Age));
    

    其实扩展方法在编译器层面实现起来非常简单,看起来就像 Java 不愿意站在原来比自己小的人的肩膀上...

    我只是随便灌灌水,老赵表激动哈

  4. 老赵
    admin
    链接

    老赵 2010-06-06 19:48:05

    @xiaowen

    如果Java设计者真这么想那也太狭隘了,我不信。而且要说“不借鉴C#”,不也已经有过foreach,params类似的功能了吗?而且似乎类似扩展方法的特性的确已经在draft里了。关键是,如果因为这种“避嫌”等非技术方面的问题,导致千百万Java用户体验不爽,这罪过可就大了……

    话说回来,扩展方法在这里还真不算是什么问题了,这里C#就算不用扩展方法,光看Lambda也比Java好看,不是么……

  5. 链接

    幸存者 2010-06-06 23:52:08

    在这里我们重载了MyClass的hashCode方法

    这里是想说override吗?重载是指overload吧

  6. outman
    124.117.97.*
    链接

    outman 2010-06-07 00:09:06

    俺只会拿来用。。

  7. 老赵
    admin
    链接

    老赵 2010-06-07 00:34:07

    @幸存者

    多谢提醒,改好了。

  8. 老赵
    admin
    链接

    老赵 2010-06-07 00:34:27

    @outman

    我说的就是“拿来用”的东西。

  9. 躺着读书
    124.234.184.*
    链接

    躺着读书 2010-06-07 01:11:46

    闭包内抛异常的情况应该比较少吧。

    而且从逻辑上说必包内也不应该处理异常。必包被认为是一个没有side effect的代码块,而异常是程序的未知路径。这样抛给外部处理也是合理的。我认为对IO的处理是不应该放在必包中的。

    就我现在接触的java来看,把异常放到RuntimeExeption下或者Throwable下的是绝大多数。硬伤不是到处都有的,呵呵。这样的硬伤在现在的java已经很少了。

  10. 躺着读书
    124.234.184.*
    链接

    躺着读书 2010-06-07 01:15:25

    不过这样的设计,确实雷到我了……

    但是看过oracle plsql的诸如decode之流的函数,我懂了……

  11. 链接

    Eric Poon 2010-06-07 01:24:28

    Java7也有Func這個類型?還是習慣了C#中的Func。部份Java代碼看起來有些混淆。如:

    Func<int, int> func = #(int x)(x * hashCode());
    

    是否應為:

    #(int,int) func = #(int x)(x * hashCode());
    
  12. 链接

    Ivony 2010-06-07 01:29:55

    我想这是Java发展的必然,其实纵观程序设计语言的历史,简单好用的语言从来都是出自于一两个设计师之手。像Java这样松散的体系,在语言的发展方面几个回合就被C#远远的抛在脑后了。

    C#的很多设计的确是有意避开Java的问题,当然也不好说是不是C#借鉴了Java的教训。我想说的是,事实上Java从一开始就是一门失败的语言,它的成功运气的成分更多,或者说JVM平台的成功要远大于Java的成功。语言的失败并不影响其流行程度,事实上世界上有语义含糊、设计失败的程序设计语言很流行。但在语言与平台挂钩的年代下,一个语言的失败并不影响其流行程度(只要平台流行)。这种局面直到2000年.NET平台的出现才得以改变。在这一方面所有的程序员都应当感谢微软作出的贡献,尽管JVM绝对具备多语言的能力,但事实上Java的设计者只是想出了J2EE和J2ME这样的怪胎。

    当然,从某种意义上来说,所有的程序设计语言,都是失败的。或者说随着历史的变迁,都会过时。C是一个异常成功的语言,他影响了很多代程序员的思维,风格也一直传承到现在。当然C也不是一个完美的语言,事实上除了受C风格的影响语言圈子,其他的语言绝大多数都选择将函数的返回值类型后置(当然还有变量类型)。想必也有非常多的C#程序员因为泛型类型返回值被智能提示郁闷过。

  13. 链接

    Ivony 2010-06-07 03:18:18

    SAM应该只是一个兼容举措,即兼容之前的用接口来表达的委托类型。

    Java一直习惯用SAM来表示一个C#里面的委托。

    看这种语法,看来Java是直接支持委托类型了。

  14. xiaowen
    123.152.43.*
    链接

    xiaowen 2010-06-07 04:06:34

    @老赵

    不过扩展方法在这里还真不算是什么问题了,这里C#就算不用扩展方法,光看Lambda也比Java好看,不是么……

    C# 3.0 的新特性,譬如 var、扩展方法、Lambda表达式、匿名类型 等,可以说都是为 linq 准备的。 特别是类型推断,主要目的就是为了让 linq 更简洁,更富表达力。

    照我看, Java Lambda 没有类型推断是因为没有明确的应用场合, 实现起来也更加复杂, 再加上如果想实现一个跟C#不大一样的Lambda(又瞎猜了,呵呵), 做成现在这样也就不奇怪了。

  15. xiaowen
    123.152.43.*
    链接

    xiaowen 2010-06-07 04:08:29

    @Ivony

    如果SAM可以看作是委托,那最多就只是个半吊子的委托,还是要自己实现 add/remove。

  16. xiaowen
    123.152.43.*
    链接

    xiaowen 2010-06-07 04:12:16

    @Eric Poon: Java7也有Func這個類型?

    老赵在这里写的 Func,应该就只是表示一开始定义的那个接口

    public interface Func<T, R> {
        R invoke(T arg);
    }
    
  17. 老赵
    admin
    链接

    老赵 2010-06-07 09:11:58

    @躺着读书

    我不觉得在闭包里抛出异常是少见或是不合理的做法啊。Java的问题是一旦遇到这样的情况(不仅仅是IO操作才会有异常啊),就变得不好用了。就如文章里说了,如果没有Checked Exception,那么我们还可以这样处理,这很合理:

    try {
        map(array, (int i)(...));
    } catch (Exception ex) {
        ...
    }
    

    而现在到了Java里,我们就没法做了,至少得这样:

    map(array, (int i) {
        try {
            ...
        } catch (Exception ex) {
            ...
        }
    });
    

    而且关键是,catch了之后又该怎么样呢?没有办法汇集到外部去,因为闭包本身的“契约”已经不允许抛出任何异常了。

  18. 老赵
    admin
    链接

    老赵 2010-06-07 09:13:31

    @xiaowen: 照我看, Java Lambda 没有类型推断是因为没有明确的应用场合。

    应用场合已经很多了,完全可以想象的到,C#就是个很好的例子,不是么,我不觉得它会放弃C#的常用场景。现在看来,Java里的闭包的主要作用可能更像是方便回调吧。像C#里那样“声明式编程”的体验比想象重要差很多。

    @xiaowen: C# 3.0 的新特性,譬如 var、扩展方法、Lambda表达式、匿名类型 等,可以说都是为 linq 准备的。特别是类型推断,主要目的就是为了让 linq 更简洁,更富表达力。

    这个说法我基本同意。LINQ的七个特性是成一个系统的,缺一个就会少一分编程体验。

  19. 老赵
    admin
    链接

    老赵 2010-06-07 09:53:28

    @Eric Poon: Java7也有Func這個類型?還是習慣了C#中的Func。

    在说SAM呢,自然要用Func接口,呵呵。

  20. ITPerson
    221.239.61.*
    链接

    ITPerson 2010-06-07 10:28:47

    老赵是否想过自己创造一种语言呢,把你欣赏的各种特性放在一起.

  21. 老赵
    admin
    链接

    老赵 2010-06-07 10:31:12

    @ITPerson

    实现一个理想中的语言不难,难的是怎么让大家一起来用,光自己玩没意思的。

  22. 链接

    facingwaller 2010-06-07 11:13:05

    题外话。 突然想到,应该可以在博客园的博客里用JS实现 提交评论也往这里发一份啊。 实现不了吗? 这里的发博客园应该更好办。 只是评论就是匿名了。 旁边注明就好了。 或者找个其他的方式实现,两边评论同步~~

  23. 老赵
    admin
    链接

    老赵 2010-06-07 11:14:33

    @facingwaller

    两边评论系统不一样,怎么同步?我最得意的地方就是这个强大的评论功能啊,呵呵。

  24. 链接

    facingwaller 2010-06-07 11:19:22

    。。。显示在页面的内容都是一样的啊, 大不了转换一下。。 我不是很了解细节, 起码作为一个用户。我看到的都是些字,为什么不能同步那些字?

  25. 链接

    facingwaller 2010-06-07 11:20:57

    题外话比较多。。评论好像没有即时刷新哦。

  26. 链接

    Ivony 2010-06-07 12:38:41

    那个参数的泛化,应该就是“逆变”。。。。

  27. 躺着读书
    116.77.212.*
    链接

    躺着读书 2010-06-07 13:35:01

    @老赵

    你是多年不写java了吧。

    抓住Exception后抛出一个RuntimeException就可以了。写法是繁琐一点,不过还能接受。

  28. 老赵
    admin
    链接

    老赵 2010-06-07 13:50:47

    @躺着读书

    虽然不写Java了,但还经常看看代码。

    每次都抓住一个Exception后再抛RuntimeExceptin只能是个不得已的权衡的,我真的很难接受,尤其是我写C#是这样的代码其实很多,比如这种:

    static Dictionary<char, List<string>> GetIndexByLambda(IEnumerable<string> keywords)
    {
        return keywords
            .GroupBy(k => k[0]) // 按照首字母分组
            .ToDictionary( // 构造字典
                g => g.Key, // 以每组的Key作为键
                g => g.OrderBy(k => k).ToList()); // 对每组排序并生成列表
    }
    

    所以我对于闭包里抛出异常,还有缺少类型推断,还是相当不爽的……

  29. 躺着读书
    116.77.212.*
    链接

    躺着读书 2010-06-07 14:15:53

    #void(int,int,int)(throws Exception) lambda = #(int i1, int i2, int i3){...}
    

    抛出去处理吧 呵呵

  30. 老赵
    admin
    链接

    老赵 2010-06-07 14:18:27

    @躺着读书

    也只能这样了,呵呵。

    不过每个闭包都声明为抛异常的话,外面的try...catch也就没法省下了……不管怎么样都很难得到一个比较美好的解决方案了,所以我说对于美妙的Lambda语法来说,似乎Checked Exception算是一个硬伤。

  31. ray58750034
    68.68.44.*
    链接

    ray58750034 2010-06-07 14:28:33

    第一眼看上去,我觉得Java闭包的语法比C#好理解,而C#的语法则更灵活一点。

    对于Java的闭包我是这样理解的, # 是声明一个匿名类的关键字,而这个匿名类需继承SAM类型。 至于C#的闭包,我之前用过,确实很顺手。 但语法始终感觉很奇怪(有种不正统的感觉)。 感觉=>像是一个操作符,但=>之后的部分,又找不到对应的语法来解释。所以当时学的时候,我是把它作为新语法硬背下来了。

  32. 躺着读书
    116.77.212.*
    链接

    躺着读书 2010-06-07 14:57:40

    java的闭包应该就是接口。 闭包可以直接赋值给接口中的某个方法,不需要是SAM

    interface A { void f(int i1, int i2, int i3) throws Exception; }
    interface B { void f(int i1, int i2, int i3); }
    interface C extends A, B { }
    
    #void(int,int,int)(throws Exception) lambda = #(int i1, int i2, int i3) {/* Some code */};
    lambda.(1,2,3);
    C foo1 = lambda;
    foo1.f(3,4,7);
    

    这样看,闭包带给java的改变和c#不尽相同,很有意思。

    PS:想在你这贴代码实在不会用。点了问号,查看了帮助还是不会。firefox下……为什么不直接搞成标签的 所见即所得太恶心了。

  33. 老赵
    admin
    链接

    老赵 2010-06-07 15:03:33

    @ray58750034

    你关注一下其他语言Lambda的语法,就知道哪个正统了,要说起来可能还真是C#正统一些,当然我还不明白什么叫做正统,呵呵。

  34. 躺着读书
    116.77.212.*
    链接

    躺着读书 2010-06-07 15:06:44

    哦 搞错了,还是非要SAM不可的,看错了。

  35. 老赵
    admin
    链接

    老赵 2010-06-07 15:08:11

    @躺着读书

    没明白你的意思……不过我猜你这里第7行代码是编译不通过的,编译器是不会隐式的把一个匿名函数cast成一个匿名对象的。在Java里匿名对象和SAM都可以用Lambda来表示,但完全还是两种不同的东西。

    说到SAM,的确是Java的Lambda的一个亮点吧,虽然在我看来会有点歧义。

    PS: 仔细看看帮助吧,写的足够详细了。现在还是使用Markdown标签,只不过有即使预览而已,呵呵。

  36. 链接

    Ivony 2010-06-07 15:43:27

    觉得lambda奇怪的其实可以用匿名方法,匿名方法更倾向于传统的C语法

    至于委托类型的定义,则是见仁见智的问题了,的确C#没有一个简单的语法来直接用函数签名定义委托类型,只能借助泛型委托的方式。但这也未必是劣势,毕竟一个命名的委托类型比Func要语义清晰。不过很多时候我们只是需要一个函数的话就显得累赘。

  37. 链接

    driedbeef 2010-06-07 22:34:24

    Java的Lambda表达式远没有C#的来得简洁清晰和有表达力。

    在我刚接触C# 3的时候,是对它的诸多语法糖有些不屑,甚至觉得它们很肤浅。后来发现原来肤浅的是自己。扩展方法能够极大地改善代码的组织,增强可读性和美感。var 这种伪关键字更是将C#的强类型特性发挥到了极致。而且,诸多新特性组合在一起形成LINQ这幅大图景之后,简直是带来了一种设计上的艺术感,这是我在任何其他编程语言上都没有看到过的。

  38. 邬杰
    122.97.16.*
    链接

    邬杰 2010-06-08 09:56:42

    JDK7的开发是与社区高度互动的,我想在这个也是他的优点所在,lambda作为一个JDK7子工程出现,http://openjdk.java.net/projects/lambda/ 大家有什么想法可以向openjdk社区反映,这样让jdk的开发者听到社区的声音。想了解一些问题产生的背景可以订阅开发者列表。

    还有JDK是一个平台级的软件,而且年龄达15年之久,每一个新的语言级特性的添加都要经过严格的论证。所以在这方便他发展比较慢

  39. lee51076008
    222.82.74.*
    链接

    lee51076008 2010-06-08 11:28:55

    @邬杰: 还有JDK是一个平台级的软件,而且年龄达15年之久,每一个新的语言级特性的添加都要经过严格的论证。所以在这方便他发展比较慢

    1. 你的意思是说,C#语言不够严谨,或者说,C#的设计没有经过严格的讨论?
    2. JAVA语言“经过严格的论证”,就讨论出了这些特性?(指java语言的泛型,特性,lambda表达式等等...)
  40. 老赵
    admin
    链接

    老赵 2010-06-08 11:30:44

    @邬杰

    好像已经晚了,已经定型了。

    其实你也可以像微软提C#的设计方式,只是采纳与否要看微软的——就像Java这边最终定下哪个设计还是要看“组织”的。Lambda就是一例,我感觉Java采用了一种挺次的Lambda方式。例如,难道之前没有人提过要对参数列表需要类型推断吗?而且当年还差点实现了这样的设计呢:

    map(array, (User u) => u.Age);
    

    虽然还是没有类型推断,但至少不像现在这样非得加括号吧……

  41. 链接

    装配脑袋 2010-06-08 19:19:59

    λx.y 才是正统的λ表达式。而且“正统”的λ表达式应该只支持α变换和β归约两种规则。。。

  42. 链接

    装配脑袋 2010-06-08 19:36:17

    其实可以用Fix来检验Java的λ表达式支持情况。看能不能实现和使用Fix来实现递归逻辑。其实C#就几乎不能用自己的λ表达式定义Fix,而是需要借助正常函数。呵呵~

  43. 链接

    陈梓瀚(vczh) 2010-06-08 19:42:48

    装配脑袋,请问你怎么用C#的Func<囧>来描述Fix的类型……

  44. 链接

    装配脑袋 2010-06-08 20:32:12

    @陈梓瀚(vczh)

    不能用Func<囧>来描述的,要专门定义个递归型的委托。或者捏。。dynamic喽 参见:这里

  45. 老吴
    116.24.51.*
    链接

    老吴 2010-06-22 21:53:32

    看了老赵的评价,对未来Java 7的闭包也有了初步的了解,这点非常感谢。在此发表一下个人的一点想法,供大家参考:

    1. 从现在看C#的实现确实要优于java的实现,特别是在类型推断和拓展方法的辅助下。
    2. 但是java也有其优势:没有了类型推断估计很难写出非常复杂的闭包,可能有助于代码阅读,特别是对于大型项目,团队内成员层次参差不齐,lamla容易写出“太优雅”的代码。闭包的实现是带着包袱的,大量的java程序的存在,必须兼容他们。

    总结,C#的公司要优于java的公司(当然是指sun),但是C#毕竟没有得到开源的广泛支持,在语言级别不如的情况下,Java仍然能够有Spring,Hibernate等优秀的框架来支撑企业开发,可见,大众的力量,也许java只要函数式编程,实现追赶上一点点C#,可能开源会把这种力量成倍的放大。

    我也是C#的程序员,同时我也很喜欢java,非常想看到java支持闭包会不会出现一个能够给linq重重冲击的开源技术。

  46. 老赵
    admin
    链接

    老赵 2010-06-22 23:07:36

    @老吴

    我只希望更多人放弃Java,支持Scala,呵呵。

  47. cc
    123.138.30.*
    链接

    cc 2010-06-23 01:13:49

    其实想说的c#语言在linux上支持太次了。

  48. lxu4net
    61.172.204.*
    链接

    lxu4net 2010-06-23 09:47:14

    能不能向不了解lambda的JAVA程序员介绍一下lambda和Anonymous Inner Class相比到底有什么很大的优势。

    C++的前车之鉴告诉我们在程序语言中集中太多的范型无论是在学习曲线、设计使用上都会付出太多的成本。

  49. 链接

    slicelee 2010-07-02 13:50:41

    C#的优雅VS有很大功劳。 lambda的优雅,以至linq用起来的小淫荡小淫荡,类型的推导是你写代码的时候IDE干的呢。

  50. 加浓胖虫
    59.151.112.*
    链接

    加浓胖虫 2010-08-06 14:11:00

    @老赵

    看了Java的lambda后我的感觉就是丑,好像是为了Lambda而Lambda的。看到你写的文章里它和checked exception的集成就更丑了。

    不过我想的更多的是Java7的JVM如果原生的支持了Lambda表达式的话,对Scala会有什么影响呢?是不是Scala在今后在处理Function的时候就可以减少一些语法糖了。

  51. 老赵
    admin
    链接

    老赵 2010-08-06 14:39:31

    @加浓胖虫

    Java 7的Lambda只是语言/编译器上的改进,和JVM无关,不会影响JVM上的其他语言。

  52. 老赵
    admin
    链接

    老赵 2010-08-06 14:41:13

    @lxu4net: 向不了解lambda的JAVA程序员介绍一下lambda和Anonymous Inner Class相比到底有什么很大的优势。C++的前车之鉴告诉我们在程序语言中集中太多的范型无论是在学习曲线、设计使用上都会付出太多的成本。

    C++的问题不在于融入太多范型,而是融入的不好,C#这点做的很好,其实就我的观点来说,看看C#就知道Java可以做成什么样了。

  53. freedom
    119.145.5.*
    链接

    freedom 2010-11-10 15:40:32

    Java,落寞了。 在语言自身上的发展,已远远落后于C#了。 很多需求需要写一堆难看的代码自己实现,又或者求助于第三方库。

  54. jamesqiu
    115.170.225.*
    链接

    jamesqiu 2011-02-08 08:59:54

    Java7借鉴一下Clojure的多好:

    \#(* %1 %2)
    
    (fn [x y] (* x y))
    
  55. 链接

    jianpingdu2009 2011-10-12 16:11:18

    当一代人走了,他们的思想也就走了。留下的只是那些我们看得见,摸得着的东西。仅仅凭借这些东西 你就能理解java的本质思想吗? 仅仅通过比较来比较去,就能理解事物的本质吗?我熟悉java,接触.net时,我发觉完全是另一种编程思维。那叫痛不欲生。如果说,c#或者java,谁避开谁,谁绕开谁,谁甩开谁。。那就相当于,一群人的思想要甩开另一群人的思想。我很好奇,当一群人,在理解一种思想的同时,还要开创另一种思想,我真的很难相信在编程领域里竟然有如此多的智者。java的原创队伍走了,思想也就走了,java或许就此落寞。而表现在我们编程领域中的东西,只是我们的编码方式,仅此而已。以后的好与坏,就看后来人。不要说,c#把java甩的很远,归根结底是经济实力把sun甩的很远。如果不是这个原因的话,webform怎会被mvc3替代,金钱做保证的。webform的失败,是彻底的否定了一代人的思想。而新的思想就是返璞归真--mvc。当页面编程语言趋于统一,社会资源趋于统一,计算平台趋于统一,编程语言的大部分特性也将趋于统一,独裁终究是独裁,迟早会over的。当.net的原创团队离去时,.net也会失去思想的。

  56. vczh
    123.116.122.*
    链接

    vczh 2011-10-15 20:21:05

    Oracle也相当有钱(java7的时候sun已经不存在了),你的论点不成立。而且M$的制度一般倾向于阻止任何一个人成为团队里面的技术类的独裁者。

  57. 老赵
    admin
    链接

    老赵 2011-10-24 19:58:37

    @jianpingdu2009

    思想当然是有高低的,你说Java和C#是两种思想,我同意啊,只是Java的思想太糟糕而已,早被C#甩开很多条马路了。

    还有别说WebForm被MVC替代,替代都是你们自己一厢情愿的,微软可从来没这么说,还有.NET原创团队之说,实在搞不懂你为什么会觉得思想是没法传承的。

    太僵化了。

  58. wmk
    116.236.180.*
    链接

    wmk 2012-01-20 16:58:49

    lambda x,y: int(x)+int(y)
    

    Python的Lambda语法,看起来也比Java的舒服多了。

  59. abcdefg123456
    222.65.251.*
    链接

    abcdefg123456 2012-02-15 23:06:42

    老赵,你的"java的思想太糟糕“,足以说明你对于编程语言的理解太过肤浅。 有空去youtube上看看joshua bloch的视频吧,看看大师是怎么说的。 你也可以follow Kevin B,和他的guava框架,看看如何用一个你认为如此糟糕的语言,写出优秀的代码。

    老实说,比较语言的优劣没有任何意义。任何一个你知道名字的编程语言都是世界上最优秀的程序员的杰作。没有糟糕的语言,只有垃圾的程序员。 老赵,你只看到了表象,你的智慧还不足以评价任何的编程语言。

  60. 老赵
    admin
    链接

    老赵 2012-02-16 14:58:13

    @abcdefg123456

    “比较语言没有意义”,“没有糟糕的语言”,这种套话谁都会说。要看视频,我看的绝对比你多,你的眼界限制了观点而已。能用Java写出优秀的框架又如何,用优秀的语言可以写的更好用,更省力。

  61. 爱吃油的鱼
    180.168.63.*
    链接

    爱吃油的鱼 2012-05-09 17:37:25

    我爱C#,强大的C#,爱上你的以后,我便忘了C++,C++就像我的初恋,你是我的下半辈子

  62. 呵呵
    221.2.138.*
    链接

    呵呵 2014-03-13 11:49:06

    本人很多语言都用过,更熟悉的、用的更多的是Java,语言本身都有优缺点,个人并没有Java情节,很愿意看到具体的有内容的批评,语言只是工具,都有过时的时候,思想才是永远留给自己的(本人作者又会说这是套话了吧)。 本来看文章还是言之有物的,有些也表示赞同,但看了评论,我只想呵呵了,这里明显是一个语言阵营的地盘,但凡发出不同声音的都来错地方了。

    @邬杰 只是陈述了一个事实,@lee51076008 好敏感,怎么就觉得人家是有言外之意的? 本人作者不只一次提到c#甩开java 几条马路,说出几条做为依据才能更有说服力,可是都只是空洞的说说而已,还打了一个自以为是的爱因斯坦和小学生的比方。。。

    奉劝一下,要真有心探讨技术,就别带这么强的个人感情,更别自以为是,容不的一点反对意见,你怎么就知道你看的视频绝对比别人多了?你又怎么确定你的眼界比别人的宽了?呵呵,你的个人博客是给自己心里满足的地方

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我