背后的故事之 - 快乐的Lambda表达式(一)

原文地址:点击打开链接

自从Lambda随.NET Framework3.5出现在.NET开发者眼前以来,它已经给我们带来了太多的欣喜。它优雅,对开发者更友好,能提高开发效率,天啊!它还有可能降低发生一些潜在错误的可能。LINQ包括ASP.NET MVC中的很多功能都是用Lambda实现的。我只能说自从用了Lambda,我腰也不酸了,腿也不疼了,手指也不抽筋了,就连写代码bug都少了。小伙伴们,你们今天用Lambda了么?但是你真的了解它么?今天我们就来好好的认识一下吧。

本文会介绍到一些Lambda的基础知识,然后会有一个小小的性能测试对比Lambda表达式和普通方法的性能,接着我们会通过IL来深入了解Lambda到底是什么,最后我们将用Lambda表达式来实现一些JavaScript里面比较常见的模式。

 

了解Lambda   

在.NET 1.0的时候,大家都知道我们经常用到的是委托。有了委托呢,我们就可以像传递变量一样的传递方法。在一定程序上来讲,委托是一种强类型的托管的方法指针,曾经也一时被我们用的那叫一个广泛呀,但是总的来说委托使用起来还是有一些繁琐。来看看使用一个委托一共要以下几个步骤:

  1. 用delegate关键字创建一个委托,包括声明返回值和参数类型
  2. 使用的地方接收这个委托
  3. 创建这个委托的实例并指定一个返回值和参数类型匹配的方法传递过去

复杂吗?好吧,也许06年你说不复杂,但是现在,真的挺复杂的。

后来,幸运的是.NET 2.0为了们带来了泛型。于是我们有了泛型类,泛型方法,更重要的是泛型委托。最终 在.NET3.5的时候,我们Microsoft的兄弟们终于意识到其实我们只需要2个泛型委托(使用了重载)就可以覆盖99%的使用场景了。

  1. Action 没有输入参数和返回值的泛型委托
  2. Action<T1, …, T16> 可以接收1个到16个参数的无返回值泛型委托
  3. Func<T1, …, T16, Tout> 可以接收0到16个参数并且有返回值的泛型委托

这样我们就可以跳过上面的第一步了,不过第2步还是必须的,只是用Action或者Func替换了。别忘了在.NET2.0的时候我们还有匿名方法,虽然它没怎么流行起来,但是我们也给它 一个露脸的机会。


1

2

3

Func<double, double> square = delegate (double x) {

    return x * x;

}

最后,终于轮到我们的Lambda优雅的登场了。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

// 编译器不知道后面到底是什么玩意,所以我们这里不能用var关键字

Action dummyLambda = () => { Console.WriteLine("Hello World from a Lambda expression!"); };

 

// double y = square(25);

Func<double, double> square = x => x * x;

 

// double z = product(9, 5);

Func<double, double, double> product = (x, y) => x * y;

 

// printProduct(9, 5);

Action<double, double> printProduct = (x, y) => { Console.WriteLine(x * y); };

 

// var sum = dotProduct(new double[] { 1, 2, 3 }, new double[] { 4, 5, 6 });

Func<double[], double[], double> dotProduct = (x, y) =>

{

    var dim = Math.Min(x.Length, y.Length);

    var sum = 0.0;

    for (var i = 0; i != dim; i++)

        sum += x[i] + y[i];

    return sum;

};

 

// var result = matrixVectorProductAsync(...);

Func<double, double, Task<double>> matrixVectorProductAsync = async (x, y) =>

{

    var sum = 0.0;

    /* do some stuff using await ... */

    return sum;

};

从上面的代码中我们可以看出:

  1. 如果只有一个参数,不需要写()
  2. 如果只有一条执行语句,并且我们要返回它,就不需要{},并且不用写return
  3. Lambda可以异步执行,只要在前面加上async关键字即可
  4. Var关键字在大多数情况下都不能使用

当然,关于最后一条,以下这些情况下我们还是可以用var关键字的。原因很简单,我们告诉编译器,后面是个什么类型就可以了。


1

2

3

4

5

6

7

8

9

Func<double,double> square = (double x) => x * x;

 

Func<string,int> stringLengthSquare = (string s) => s.Length * s.Length;

 

Action<decimal,string> squareAndOutput = (decimal x, string s) =>

{

    var sqz = x * x;

    Console.WriteLine("Information by {0}: the square of {1} is {2}.", s, x, sqz);

};

现在,我们已经知道Lambda的一些基本用法了,如果仅仅就这些东西,那就不叫快乐的Lambda表达式了,让我们看看下面的代码。


1

2

3

4

5

var a = 5;

Func<int,int> multiplyWith = x => x * a;

var result1 = multiplyWith(10); //50

a = 10;

var result2 = multiplyWith(10); //100

是不是有一点感觉了?我们可以在Lambda表达式中用到外面的变量,没错,也就是传说中的闭包啦。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

void DoSomeStuff()

{

    var coeff = 10;

    Func<int,int> compute = x => coeff * x;

    Action modifier = () =>

    {

        coeff = 5;

    };

 

    var result1 = DoMoreStuff(compute);

 

    ModifyStuff(modifier);

 

    var result2 = DoMoreStuff(compute);

}

 

int DoMoreStuff(Func<int,int> computer)

{

    return computer(5);

}

 

void ModifyStuff(Action modifier)

{

    modifier();

}

在上面的代码中,DoSomeStuff方法里面的变量coeff实际是由外部方法ModifyStuff修改的,也就是说ModifyStuff这个方法拥有了访问DoSomeStuff里面一个局部变量的能力。它是如何做到的?我们马上会说的J。当然,这个变量作用域的问题也是在使用闭包时应该注意的地方,稍有不慎就有可能会引发你想不到的后果。看看下面这个你就知道了。


1

2

3

4

5

6

7

8

9

var buttons = new Button[10];

 

for (var i = 0; i < buttons.Length; i++)

{

    var button = new Button();

    button.Text = (i + 1) + ". Button - Click for Index!";

    button.OnClick += (s, e) => { Messagebox.Show(i.ToString()); };

    buttons[i] = button;

}

猜猜你点击这些按钮的结果是什么?是”1, 2, 3…”。但是,其实真正的结果是全部都显示10。为什么?不明觉历了吧?那么如果避免这种情况呢?


1

2

3

4

5

var button = new Button();

var index = i;

button.Text = (i + 1) + ". Button - Click for Index!";

button.OnClick += (s, e) => { Messagebox.Show(index.ToString()); };

buttons[i] = button;

其实做法很简单,就是在for的循环里面把当前的i保存下来,那么每一个表达式里面存储的值就不一样了。

接下来,我们整点高级的货,和Lambda息息相关的表达式(Expression)。为什么说什么息息相关,因为我们可以用一个Expression将一个Lambda保存起来。并且允许我们在运行时去解释这个Lambda表达式。来看一下下面简单的代码:


1

2

3

Expression<Func<MyModel, int>> expr = model => model.MyProperty;

var member = expr.Body as MemberExpression;

var propertyName = member.Expression.Member.Name;

这个的确是Expression最简单的用法之一,我们用expr存储了后面的表达式。编译器会为我们生成表达式树,在表达式树中包括了一个元数据像参数的类型,名称还有方法体等等。在LINQ TO SQL中就是通过这种方法将我们设置的条件通过where扩展方法传递给后面的LINQ Provider进行解释的,而LINQ Provider解释的过程实际上就是将表达式树转换成SQL语句的过程。

 

Lambda表达式的性能

关于Lambda性能的问题,我们首先可能会问它是比普通的方法快呢?还是慢呢?接下来我们就来一探究竟。首先我们通过一段代码来测试一下普通方法和Lambda表达 式之间的性能差异。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

class StandardBenchmark : Benchmark

{

    const int LENGTH = 100000;

    static double[] A;

    static double[] B;

 

    static void Init()

    {

        var r = new Random();

        A = new double[LENGTH];

        B = new double[LENGTH];

 

        for (var i = 0; i < LENGTH; i++)

        {

            A[i] = r.NextDouble();

            B[i] = r.NextDouble();

        }

    }

 

    static long LambdaBenchmark()

    {

        Func<double> Perform = () =>

        {

            var sum = 0.0;

 

            for (var i = 0; i < LENGTH; i++)

                sum += A[i] * B[i];

 

            return sum;

        };

        var iterations = new double[100];

        var timing = new Stopwatch();

        timing.Start();

 

        for (var j = 0; j < iterations.Length; j++)

            iterations[j] = Perform();

 

        timing.Stop();

        Console.WriteLine("Time for Lambda-Benchmark: \t {0}ms", timing.ElapsedMilliseconds);

        return timing.ElapsedMilliseconds;

    }

 

    static long NormalBenchmark()

    {

        var iterations = new double[100];

        var timing = new Stopwatch();

        timing.Start();

 

        for (var j = 0; j < iterations.Length; j++)

            iterations[j] = NormalPerform();

 

        timing.Stop();

        Console.WriteLine("Time for Normal-Benchmark: \t {0}ms", timing.ElapsedMilliseconds);

        return timing.ElapsedMilliseconds;

    }

 

    static double NormalPerform()

    {

        var sum = 0.0;

 

        for (var i = 0; i < LENGTH; i++)

            sum += A[i] * B[i];

 

        return sum;

    }

}

}

代码很简单,我们通过执行同样的代码来比较,一个放在Lambda表达式里,一个放在普通的方法里面。通过4次测试得到如下结果:

Lambda Normal-Method

70ms  84ms
73ms  69ms
92ms  71ms
87ms  74ms

按理来说,Lambda应该是要比普通方法慢很小一点点的,但是不明白第一次的时候为什么Lambda会比普通方法还快一点。- -!不过通过这样的对比我想至少可以说明Lambda和普通方法之间的性能其实几乎是没有区别的。

那么Lambda在经过编译之后会变成什么样子呢?让LINQPad告诉你。

上图中的Lambda表达式是这样的:


1

2

3

4

Action<string> DoSomethingLambda = (s) =>

{

    Console.WriteLine(s);// + local

};

对应的普通方法的写法是这样的:


1

2

3

4

void DoSomethingNormal(string s)

{

    Console.WriteLine(s);

}

上面两段代码生成的IL代码呢?是这样地:


1

2

3

4

5

6

7

8

9

10

11

12

DoSomethingNormal:

IL_0000:  nop       

IL_0001:  ldarg.1   

IL_0002:  call        System.Console.WriteLine

IL_0007:  nop       

IL_0008:  ret       

<Main>b__0:

IL_0000:  nop       

IL_0001:  ldarg.0   

IL_0002:  call        System.Console.WriteLine

IL_0007:  nop       

IL_0008:  ret

最大的不同就是方法的名称以及方法的使用而不是声明,声明实际上是一样的。通过上面的IL代码我们可以看出,这个表达式实际被编译器取了一个名称,同样被放在了当前的类里面。所以实际上,和我们调类里面的方法没有什么两样。下面这张图说明了这个编译的过程:

上面的代码中没有用到外部变量,接下来我们来看另外一个例子。

 


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

void Main()

{

    int local = 5;

 

    Action<string> DoSomethingLambda = (s) => {

        Console.WriteLine(s + local);

    };

 

    global = local;

 

    DoSomethingLambda("Test 1");

    DoSomethingNormal("Test 2");

}

 

int global;

 

void DoSomethingNormal(string s)

{

    Console.WriteLine(s + global);

}

这次的IL代码会有什么不同么?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

IL_0000:  newobj      UserQuery+<>c__DisplayClass1..ctor

IL_0005:  stloc.1   

IL_0006:  nop       

IL_0007:  ldloc.1   

IL_0008:  ldc.i4.5  

IL_0009:  stfld       UserQuery+<>c__DisplayClass1.local

IL_000E:  ldloc.1   

IL_000F:  ldftn       UserQuery+<>c__DisplayClass1.<Main>b__0

IL_0015:  newobj      System.Action<System.String>..ctor

IL_001A:  stloc.0   

IL_001B:  ldarg.0   

IL_001C:  ldloc.1   

IL_001D:  ldfld       UserQuery+<>c__DisplayClass1.local

IL_0022:  stfld       UserQuery.global

IL_0027:  ldloc.0   

IL_0028:  ldstr       "Test 1"

IL_002D:  callvirt    System.Action<System.String>.Invoke

IL_0032:  nop       

IL_0033:  ldarg.0   

IL_0034:  ldstr       "Test 2"

IL_0039:  call        UserQuery.DoSomethingNormal

IL_003E:  nop       

 

DoSomethingNormal:

IL_0000:  nop       

IL_0001:  ldarg.1   

IL_0002:  ldarg.0   

IL_0003:  ldfld       UserQuery.global

IL_0008:  box         System.Int32

IL_000D:  call        System.String.Concat

IL_0012:  call        System.Console.WriteLine

IL_0017:  nop       

IL_0018:  ret       

 

<>c__DisplayClass1.<Main>b__0:

IL_0000:  nop       

IL_0001:  ldarg.1   

IL_0002:  ldarg.0   

IL_0003:  ldfld       UserQuery+<>c__DisplayClass1.local

IL_0008:  box         System.Int32

IL_000D:  call        System.String.Concat

IL_0012:  call        System.Console.WriteLine

IL_0017:  nop       

IL_0018:  ret       

 

<>c__DisplayClass1..ctor:

IL_0000:  ldarg.0   

IL_0001:  call        System.Object..ctor

IL_0006:  ret

你发现了吗?两个方法所编译出来的内容是一样的, DoSomtingNormal和<>c__DisplayClass1.<Main>b__0,它们里面的内容是一样的。但是最大的不一样,请注意了。当我们的Lambda表达式里面用到了外部变量的时候,编译器会为这个Lambda生成一个类,在这个类中包含了我们表达式方法。在使用这个Lambda表达式的地方呢,实际上是new了这个类的一个实例进行调用。这样的话,我们表达式里面的外部变量,也就是上面代码中用到的local实际上是以一个全局变量的身份存在于这个实例中的。

用Lambda表达式实现一些在JavaScript中流行的模式

说到JavaScript,最近几年真是风声水起。不光可以应用所有我们软件工程现存的一些设计模式,并且由于它的灵活性,还有一些由于JavaScript特性而产生的模式。比如说模块化,立即执行方法体等。.NET由于是强类型编译型的语言,灵活性自然不如JavaScript,但是这并不意味着JavaScript能做的事情.NET就不能做,下面我们就来实现一些JavaScript中好玩的写法。

回调模式

回调模式也并非JavaScript特有,其实在.NET1.0的时候,我们就可以用委托来实现回调了。但是今天我们要实现的回调可就不一样了。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

void CreateTextBox()

{

    var tb = new TextBox();

    tb.IsReadOnly = true;

    tb.Text = "Please wait ...";

    DoSomeStuff(() => {

        tb.Text = string.Empty;

        tb.IsReadOnly = false;

    });

}

 

void DoSomeStuff(Action callback)

{

    // Do some stuff - asynchronous would be helpful ...

    callback();

}

上面的代码中,我们在DoSomeStuff完成之后,再做一些事情。这种写法在JavaScript中是很常见的,jQuery中的Ajax的oncompleted, onsuccess不就是这样实现的么?又或者LINQ扩展方法中的foreach不也是这样的么?

返回方法

我们在JavaScript中可以直接return一个方法,在.net中虽然不能直接返回方法,但是我们可以返回一个表达式。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

Func<string, string> SayMyName(string language)

{

    switch(language.ToLower())

    {

        case "fr":

            return name => {

                return "Je m'appelle " + name + ".";

            };

        case "de":

            return name => {

                return "Mein Name ist " + name + ".";

            };

        default:

            return name => {

                return "My name is " + name + ".";

            };

    }

}

 

void Main()

{

    var lang = "de";

    //Get language - e.g. by current OS settings

    var smn = SayMyName(lang);

    var name = Console.ReadLine();

    var sentence = smn(name);

    Console.WriteLine(sentence);

}

是不是有一种策略模式的感觉?这还不够完美,这一堆的switch case看着就心烦,让我们用Dictionary<TKey,TValue>来简化它。来看看来面这货:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

static class Translations

{

    static readonly Dictionary<string, Func<string, string>> smnFunctions = new Dictionary<string, Func<string, string>>();

 

    static Translations()

    {

        smnFunctions.Add("fr", name => "Je m'appelle " + name + ".");

        smnFunctions.Add("de", name => "Mein Name ist " + name + ".");

        smnFunctions.Add("en", name => "My name is " + name + ".");

    }

 

    public static Func<string, string> GetSayMyName(string language)

    {

        //Check if the language is available has been omitted on purpose

        return smnFunctions[language];

    }

}

自定义型方法

自定义型方法在JavaScript中比较常见,主要实现思路是这个方法被设置成一个属性。在给这个属性附值,甚至执行过程中我们可以随时更改这个属性的指向,从而达到改变这个方法的目地。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

class SomeClass

{

    public Func<int> NextPrime

    {

        get;

        private set;

    }

 

    int prime;

 

    public SomeClass

    {

        NextPrime = () => {

            prime = 2;

 

            NextPrime = () => {                   // 这里可以加上 第二次和第二次以后执行NextPrive()的逻辑代码

                return prime;

            };

 

            return prime;

        }

    }

}

上面的代码中当NextPrime第一次被调用的时候是2,与此同时,我们更改了NextPrime,我们可以把它指向另外的方法,和JavaScrtip的灵活性比起来也不差吧?如果你还不满意 ,那下面的代码应该能满足你。


1

2

3

4

5

6

7

8

9

Action<int> loopBody = i => {

    if(i == 1000)

        loopBody = //把loopBody指向别的方法

 

    /* 前10000次执行下面的代码 */

};

 

for(int j = 0; j < 10000000; j++)

    loopBody(j);

在调用的地方我们不用考虑太多,然后这个方法本身就具有调优性了。我们原来的做法可能是在判断i==1000之后直接写上相应的代码,那么和现在的把该方法指向另外一个方法有什么区别呢?

自执行方法

JavaScript 中的自执行方法有以下几个优势:

  1. 不会污染全局环境
  2. 保证自执行里面的方法只会被执行一次
  3. 解释完立即执行

在C#中我们也可以有自执行的方法:


1

2

3

(() => {

    // Do Something here!

})();

上面的是没有参数的,如果你想要加入参数,也非常的简单:


1

2

3

((string s, int no) => {

    // Do Something here!

})("Example", 8);

.NET4.5最闪的新功能是什么?async?这里也可以


1

2

3

4

5

await (async (string s, int no) => {

    // 用Task异步执行这里的代码

})("Example", 8);

 

// 异步Task执行完之后的代码

对象即时初始化

大家知道.NET为我们提供了匿名对象,这使用我们可以像在JavaScript里面一样随意的创建我们想要对象。但是别忘了,JavaScript里面可以不仅可以放入数据,还可以放入方法,.NET可以么?要相信,Microsoft不会让我们失望的。


1

2

3

4

5

6

7

8

9

10

11

//Create anonymous object

var person = new {

    Name = "Jesse",

    Age = 28,

    Ask = (string question) => {

        Console.WriteLine("The answer to `" + question + "` is certainly 42!");

    }

};

 

//Execute function

person.Ask("Why are you doing this?");

但是如果你真的是运行这段代码,是会抛出异常的。问题就在这里,Lambda表达式是不允许赋值给匿名对象的。但是委托可以,所以在这里我们只需要告诉编译器,我是一个什么类型的委托即可。


1

2

3

4

5

6

7

var person = new {

    Name = "Florian",

    Age = 28,

    Ask = (Action<string>)((string question) => {

        Console.WriteLine("The answer to `" + question + "` is certainly 42!");

    })

};

但是这里还有一个问题,如果我想在Ask方法里面去访问person的某一个属性,可以么?


1

2

3

4

5

6

7

8

var person = new

{

                Name = "Jesse",

                Age = 18,

                Ask = ((Action<string>)((string question) => {

                    Console.WriteLine("The answer to '" + question + "' is certainly 20. My age is " + person.Age );

                }))

};

结果是连编译都通不过,因为person在我们的Lambda表达式这里还是没有定义的,当然不允许使用了,但是在JavaScript里面是没有问题的,怎么办呢?.NET能行么?当然行,既然它要提前定义,我们就提前定义好了。


1

2

3

4

5

6

7

8

9

10

11

dynamic person = null;

person = new {

    Name = "Jesse",

    Age = 28,

    Ask = (Action<string>)((string question) => {

        Console.WriteLine("The answer to `" + question + "` is certainly 42! My age is " + person.Age + ".");

    })

};

 

//Execute function

person.Ask("Why are you doing this?");

运行时分支

这个模式和自定义型方法有点类似,唯一的不同是它不是在定义自己,而是在定义别的方法。当然,只有当这个方法基于属性定义的时候才有这种实现的可能。


1

2

3

4

5

6

7

8

9

10

11

public Action AutoSave { get; private set; }

 

public void ReadSettings(Settings settings)

{

    /* Read some settings of the user */

 

    if(settings.EnableAutoSave)

        AutoSave = () => { /* Perform Auto Save */ };

    else

        AutoSave = () => { }; //Just do nothing!

}

可能有人会觉得这个没什么,但是仔细想想,你在外面只需要调用AutoSave就可以了,其它的都不用管。而这个AutoSave,也不用每次执行的时候都需要去检查配置文件了。

 

总结

Lambda表达式在最后编译之后实质是一个方法,而我们声明Lambda表达式呢实质上是以委托的形式传递的。当然我们还可以通过泛型表达式Expression来传递。通过Lambda表达式形成闭包,可以做很多事情,但是有一些用法现在还存在争议,本文只是做一个概述 :),如果有不妥,还请拍砖。谢谢支持 :)

还有更多Lambda表达式的新鲜玩法,请移步: 背后的故事之 – 快乐的Lambda表达式(二)

时间: 2024-12-11 01:09:56

背后的故事之 - 快乐的Lambda表达式(一)的相关文章

探索Java语言与JVM中的Lambda表达式

Lambda表达式是自Java SE 5引入泛型以来最重大的Java语言新特性,本文是2012年度最后一期Java Magazine中的一篇文章,它介绍了Lamdba的设计初衷,应用场景与基本语法.(2013.01.07最后更新) Lambda表达式,这个名字由该项目的专家组选定,描述了一种新的函数式编程结构,这个即将出现在Java SE 8中的新特性正被大家急切地等待着.有时你也会听到人们使用诸如闭包,函数直接量,匿名函数,及SAM(Single Abstract Method)这样的术语.其

Java8:Lambdas(二)学习怎样去使用lambda表达式

原文链接  作者:Ted Neward   译者:赵峰 Java SE 8的发布很快就到了.伴随着它来的不仅仅是新的语言lambda表达式(同样被称为闭包或匿名方法)--伴随着一些语言特性支持--更重要的是API和library的增强将会使传统的Java核心libraries变的更易于使用.其中大多数的增强和补充是在Collections API中,因为Collections API在整个应用中随处可见,这篇文章大部分是在讨论它. 然而 ,很有可能大多数的Java开发者将不会熟悉隐藏在lambd

匿名方法,Lambda表达式,高阶函数

原文:匿名方法,Lambda表达式,高阶函数 匿名方法 c#2.0引入匿名方法,不必创建单独的方法,因此减少了所需的编码系统开销. 常用于将委托和匿名方法关联,例如 1. 使用委托和方法关联: this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);private void btnRefresh_Click(object sender, EventArgs e){    BindData();} 2. 使用委

Java函数式编程(一):你好,Lambda表达式_java

第一章 你好,lambda表达式! 第一节 Java的编码风格正面临着翻天覆地的变化. 我们每天的工作将会变成更简单方便,更富表现力.Java这种新的编程方式早在数十年前就已经出现在别的编程语言里面了.这些新特性引入Java后,我们可以写出更简洁,优雅,表达性更强,错误更少的代码.我们可以用更少的代码来实现各种策略和设计模式. 在本书中我们将通过日常编程中的一些例子来探索函数式风格的编程.在使用这种全新的优雅的方式进行设计编码之前,我们先来看下它到底好在哪里. 改变了你的思考方式 命令式风格--

黑客精神祭 挖掘神秘黑客背后的故事

无论媒体如何渴望挖掘黑客背后的故事.黑客始终是一群神秘的存在.于是乎,笔者的这篇黑客精神祭的直接叙述对象也就指向了更为人们所知的"病毒". 病毒诞生之初--严谨之美 当你看着爱机被病毒蹂躏的时候,或许你根本无从感觉病毒之美.但是,如果你愿意纯粹以技术角度评判病毒的话,或许你能在病毒诞生之初,发现它的美.那是一种精密逻辑思维构筑下的严谨之美. 1986年,大脑病毒面世.它是世界上第一个DOS病毒,同时也开创了引导区病毒的先河.它用汇编语言写成,代码精简,有很高的编程技术.大脑病毒是用极其

Java8之使用新JS解释器Nashorn编译Lambda表达式

原文链接 作者:Tal Weiss  CEO of Takipi  译者:踏雁寻花,xbkaishui  校对:方腾飞 在最近的一篇文章中,我了解了一下Java8和Scala是如何实现 Lambda 表达式的.正如我们所知道的,Java8不仅对javac编辑器做了很大改进,它还加入了一个全新的项目-Nashorn.这个新的解释器将会代替Java现有的Rhino解释器.据说它执行JavaScript的速度非常之快,就像世界上最快的跑车 V8s,所以,我觉得现在很有必要打开Nashorn源码,看看它

如何使用 Lambda 表达式做抽象代表

Lambda表达比代表定义和带外方法定义的结合更清楚,且相关的额外工作只需要满足语言定义即可.不过,它也有一些不足之处.如果某个方法的参数包含System.Delegate 这样的抽象类型,用lambda表达式介绍特殊的问题:C#编译器不能将lambda表达式转换成还未明确定义的衍生代表类型. 如果不仔细思考一下,你的代码看上去就会像是来自.NET1.0的东西.在本文中,我将告诉告诉你为什么lambda表达式不足以被直接转换成抽象代表类型,并且教你怎样使得编译器转换你所定义的指定代表.解决方案依

C# 3.0新特性体验之Lambda表达式

C#2.0介绍了一个新特性--匿名方法,允许开发者在线(inline)声明自己的函数代码而无须使用委托函数(delegate function).C#3.0中提供了一个新特性--Lambda表达式,它提供了完成相同目标的更加简洁的格式.让我们在讨论Lambda表达式以前仔细研究一下匿名方法. 匿名方法 假设你需要创建一个按钮,当点击它的时候更新ListBox里的内容.在C#1.0和1.1里,你要这样做: public MyForm(){ listBox = new ListBox(...); t

Java SE 8: Lambda表达式

Java SE 8在6月13的版本中已经完全了全部的功能.在这些新的功能中,lambda表达式是推动该版本发布 的最重要新特性.因为Java第一次尝试引入函数式编程的相关内容.社区对于lambda表达式也期待已久. Lambda表达式的相关内容在JSR 335中定义,本文的内容基于最新的规范和JDK 8 Build b94. 开发环境使用 的是Eclipse. Lambda表达式 要理解lambda表达式,首先要了解的是函数式接口(functional interface).简单来说,函数式接口