您好,欢迎访问一九零五行业门户网

Java的Lambda表达式使用实例分析

lambada 简介lambda 表达式 是java 8新加入的新特性,它在java中是引入了函数式编程这一概念。那么什么是函数式编程呢?
函数式编程:函数式编程是面向数学的抽象,将计算描述为一种表达式求值。
我们平常所说的面向对象编程属于命令式编程,函数式编程和命令式编程的区别是:
函数式编程关心数据的映射,命令式编程关系解决问题的步骤。
函数式编程关系类型(代数结构)之间的关系,命令式编程关系解决问题的步骤。
函数式编程的本质:
函数式编程中的函数指的不是计算机中的函数,而是数学中的函数,即自变量的映射。即:一个函数的值仅取决于函数参数的值,不依赖其他状态。
严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其他命令式控制结构进行编程。
函数式编程的好处:
函数式编程的好处是主要是不可变性带来的。没有可变的状态,函数就是引用透明(referential transparency)的和没有副作用的(no side effect)。
上面这些都是一些基本的概念,但是我们平时可能接触这些方面的东西比较少,所以一开始感觉函数式编程是很难得东西。
简单的示例talk is cheap, show me the code!
先来一个最简单的例子,可能也是介绍的最多的例子了。哈哈!
给按钮添加监视器。
使用匿名内部类的方式,进行添加。
submit.addactionlistener(new actionlistener() { @override public void actionperformed(actionevent e) { joptionpane.showmessagedialog(null, "点击了确定按钮", "确定", joptionpane.information_message); }});
这种方式的缺点:使用了很多的模板代码,真正必要的代码,只是方法体内的代码。所以,java 8 引入的 lambda 表达式可以简化这种代码(当然了,也是有限制的,不是所有的匿名内部类都可以,这个后面会提到。)。
使用 lambda 表达式 简化代码
submit.addactionlistener((e)->{ joptionpane.showmessagedialog(null, "点击了确定按钮", "确定", joptionpane.information_message);});
lambda 表达式是一个匿名方法,将行为像数据一样进行传递。
说明
可以看出来,使用 lambda 表达式简化后的代码表达变得更加清晰了,并且不用再去写繁琐的模板代码了。
进一步简化
参数括号和代码体的花括号也可以省略(只有一个参数时,可以省略圆括号,只有一行代码时,可以省略花括号)。
actionlistener listener = e->joptionpane.showmessagedialog(null, "点击了确定按钮", "确定", joptionpane.information_message);

小结
当使用 lambda 表达式代替匿名内部类创建对象时,lambda 表达式的代码块将会替代实现抽象方法的方法体,lambda 就相当于一个匿名方法。
lambda 表达式的组成部分lambda 表达式由三部分组成:
形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,可以省略形参列表的圆括号。
箭头(->)。英文短线和大于号。
代码块。如果代码块只有一句,可以省略花括号。如果只有一条 return 语句,可以省略 return,lambda表达式会自动返回这条语句的值。
注:
之所以可以省略形参列表是因为 编译器 可以进行类型推断,例如:
list<dog> dogs1 = new arraylist<dog>();list<dog> dogs2 = new arraylist<>();
上面使用 菱形语法,可以省略尖括号里面的东西,这就是类型推断的作用。
但是类型推断也不是万能的,不是所有的都可以推断出来的,所以有时候,还是要显示的添加形参类型,例如:
先不要管这个代码的具体作用。
binaryoperator b = (x, y)->x*y;//上面这句代码无法通过编译,下面是报错信息:无法将 * 运算符作用于 java.lang.object 类型。the operator * is undefined for the argument type(s) java.lang.object, java.lang.object
//添加参数类型,正确的代码。binaryoperator<integer> b = (x, y)->x*y;
所以,类型推断不是万能的,如果编译器无法推断,那就是我们的错误,不要过度依赖编译器。有时候,显示的添加参数类型,还是很必要的,当然了,这需要去多练习。
函数式接口前面了解了,lambda 表达式可以代替匿名内部类,进而达到简化代码,表达清晰的目的。那么使用 lambda 表示式的前提是什么呢?-- 函数式接口
lambda 表达式的类型,也被称为 目标类型 (target type),它必须是一个函数式接口(functional interface)。所谓函数式接口,指的就是:只包含一个抽象方法的接口。(可以包含多个默认方法,静态方法,但必须只有一个抽象方法)。
注:java 8 专门提供了一个注解:@functionalinterface。用于标注某个接口是函数式接口,这样编译时就会检查,如果该接口含有多个抽象方法,编译器就会报错。
上面使用 lambda 表达式来为按钮添加了监视器,可以看出来,lambda 表达式 代替了 new actionlistener()对象。
所以 lambda 的表达式就是被当成一个对象。
例如:
actionlistener listener = e->joptionpane.showmessagedialog(null, "点击了确定按钮", "确定", joptionpane.information_message);

从上面这个例子中可以看出来,lambda 表达式实现的是匿名方法&ndash;因此它只能实现特定函数式接口中的唯一方法。
所以 lambda 表达式有下面两种限制:
lambda 表达式的目标类型必须是明确的函数式接口。 lambda 表达式只能为函数式接口创建对象。lambda只能实现一个方法,因此它只能为含有一个抽象方法的接口(函数式接口)创建对象。 介绍几个 java 中重要的函数接口
从这种表可以看出来,抽象方法的名字反而不是最重要的了,重要的是参数和返回值。 因为在写 lambda 表达式的时候,也不要使用 抽象方法名了。
上面使用几个简单的例子来说明上面接口的应用:
测试代码
import java.text.parseexception;import java.util.function.binaryoperator;import java.util.function.consumer;import java.util.function.function;import java.util.function.predicate;import java.util.function.supplier;import java.util.function.unaryoperator;import java.util.stream.stream;public class test { public static void main(string[] args) throws parseexception { //lambda 表达式中的构造器引用,用于简化代码。 creat<dog> c = dog::new; dog dog = c.creat("小黑", 15); system.out.println(dog.tostring()); predicate<string> predicate = (words)->{ return words.length() > 20; }; assert predicate.test("i love you yesterday and today!") : "长度小于20"; assert !predicate.test("god bless you!") : "长度小于20"; system.out.println("------------------------"); consumer<dog> consumer = system.out::println; consumer.accept(dog); system.out.println("------------------------"); function<dog, string> function = (dogobj)->{ return dogobj.getname(); }; system.out.println(function.apply(dog)); system.out.println("------------------------"); supplier<dog> supplier = ()->{ return new dog("大黄", 4); }; system.out.println(supplier.get()); //一元操作符 unaryoperator<boolean> unaryoperation = (flag)->{ return !flag; }; system.out.println(unaryoperation.apply(true)); binaryoperator<integer> binaryoperator = (x, y)->x*y; int result = binaryoperator.apply(999, 9999); system.out.println(result); }}
测试使用的实体类
public class dog { private string name; private int age; public dog(string name, int age) { this.name = name; this.age = age; } public string getname() { return name; } public void setname(string name) { this.name = name; } public int getage() { return age; } public void setage(int age) { this.age = age; } @override public string tostring() { return "dog [name=" + name + ", age=" + age + "]"; }}
自定义函数式接口
@functionalinterfacepublic interface creat<t> { public t creat(string name, int age);}
运行截图就不放了,感兴趣的可以试一下。
说明
我这里直接使用 lambda 创建了对象,然后调用了这个对象的方法(就是lambda 的代码块部分),真正使用的时候,都是直接传递 lambda 表达式的,这种方法并不推荐,但是可以让我们很好的理解为什么? 可以看出来,lambda 表达式的作用,最后还是需要调用 重写的抽象方法的,只不过使用表达更加清晰,简化了代码。
例如:
list<dog> dogs = new arraylist<>(); dogs.add(new dog("大黄", 2)); dogs.add(new dog("小黑", 3)); dogs.add(new dog("小哈",1)); //将行为像数据一样传递,使用集合的 foreach 方法来遍历集合, //参数可以是一个 lambda 表达式。 consumer<? super dog> con = (e)->{ system.out.println(e); }; dogs.foreach(con); system.out.println("--------------------------\n"); //直接传递 lambda 表达式,更加简洁 dogs.foreach(e->system.out.println(e)); system.out.println("--------------------------\n"); //使用方法引用,进一步简化(可以看我的另一篇关于方法引用的博客) dogs.foreach(system.out::println); system.out.println("--------------------------\n"); //使用 lambda 对集合进行定制排序,按照年龄排序(从小到大)。 dogs.sort((e1, e2)->e1.getage()-e2.getage()); dogs.foreach(system.out::println);
可以看出来,通过使用 lambda 表达式可以,极大的简化代码,更加方便的操作集合。值得一提的是:lambda 表达式 和 stream 的结合,可以拥有更加丰富的操作,这也是下一步学习的方向。
运行截图:
以上就是java的lambda表达式使用实例分析的详细内容。
其它类似信息

推荐信息