Java8新特性
目录
Lambda表达式
0、首先我们说说为什么叫Lambda表达式:
Lambda,为什么叫这个名字呢,在没有计算机的年代,某个逻辑学家想要形式化地表示能有效计算机地数学函数。于是用了λ(Lambda)来标记参数。
1、为什么引入Lambda表达式?
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码。
可以写出更加简洁、更加灵活的代码。作为一种更加紧凑的代码风格,使得Java语言表达能力得到了提升。
首先我们回顾以下匿名内部类
public class LambdaTest01 {
//原来的匿名内部类
@Test
public void test01(){
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//核心
return Integer.compare(o1,o2);
}
};
TreeSet<Integer> treeSet = new TreeSet<>(comparator);
}
//那么我们接下来看看Lambda表达式的语法,这下是不是就可以感受到Lambda的强大与便捷了
@Test
public void test2(){
Comparator<Integer> comparator = (o1,o2) -> Integer.compare(o1,o2);
TreeSet<Integer> treeSet = new TreeSet<>(comparator);
}
}
我们接着往下看
例如: 定义一个比较器来完成排序的功能: 如果想按照长度而不是默认的字典进行排序,可以想sort方法传入一个Comparator对象:
class LengthComparator implements Comparator<String>{
//compare方法并不是立即调用。实际上,在数组完成排序之前,sort方法会一直调用compare方法,只要元素的顺序不正确就会重新排列元素。将比较元素所需的代码段放在
//sort方法中,这个代码将与其余的排序逻辑集成
public int compare(String first,String second){return first.length)-second.length()};
}
...
Arrarys.sort(strings,new LengthComparator());
Java是一门面向对象语言,必须new一个对象,new出来的对象的类需要有一个方法包含所需要的代码。 在Java中,编写类似的API处理实现了某个特定的接口的类对象,这种API使用可能不怎么方便。
下面我们有一个需求:获取当前公司中员工年龄大于35的员工信息
List<Employee> employees = Arrays.asList(
new Employee("zs",18,10000.99),
new Employee("ls",18,14000.66),
new Employee("zl",13,30000.55),
new Employee("ww",39,10020.77),
new Employee("qq",49,22000.33)
);
//需求:获取当前公司中员工年龄大于35的员工信息
@Test
public void test03(){
List<Employee> list = filterEmployees(employees);
for (Employee employee:list){
System.out.println(employee);
}
}
public List<Employee> filterEmployees(List<Employee> list){
List<Employee> employees = new ArrayList<>();
for (Employee employee:list){
if (employee.getAge()>=35){
employees.add(employee);
}
}
return employees;
}
增加需求:获取当前公司员工工资大于15000
//增加需求:获取当前公司员工工资大于15000
@Test
public void test04(){
List<Employee> list = filterEmployees2(employees);
for (Employee employee:list){
System.out.println(employee);
}
}
public List<Employee> filterEmployees2(List<Employee> list){
List<Employee> employees = new ArrayList<>();
for (Employee employee:list){
if (employee.getSalary()>=15000 && employee.getAge()>=35){
employees.add(employee);
}
}
return employees;
}
这两个需求的给我们的直观的问题就是代码冗余,除了核心逻辑的地方有区别之外,其他都是重复代码。
接下来介绍一下优化以上代码的方法:
首先创建一个MyPredicate接口
public interface MyPredicate<T> {
public boolean test(T t);
}
创建接口的实现类FilterEmployeeByAge
public class FilterEmployeeByAge implements MyPredicate<Employee>{
@Override
public boolean test(Employee employee) {
return employee.getAge()>=35;
}
}
优化方式一:策略设计模式
//以mypredicate的方式进行过滤
public List<Employee> filterEmployees3(List<Employee> list,MyPredicate<Employee> myPredicate){
List<Employee> employees = new ArrayList<>();
for (Employee employee:list){
if (myPredicate.test(employee)){
employees.add(employee);
}
}
return employees;
}
进行测试
@Test
public void test05(){
List<Employee> employees = filterEmployees3(this.employees, new FilterEmployeeByAge());
for (Employee employee:employees){
System.out.println(employee);
}
}
优化方式二、匿名内部类
//优化方式二:匿名内部类
@Test
public void test6(){
List<Employee> employees = filterEmployees3(this.employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary() <= 15000;
}
});
for (Employee employee: employees){
System.out.println(employee);
}
}
优化方式三、Lambda
//优化方式三、Lambda
@Test
public void test07(){
List<Employee> list = filterEmployees3(this.employees, (item) ->
item.getSalary() <= 15000
);
list.forEach(System.out::println);
}
优化方式四、stram流
//优化方式四、stream流
@Test
public void test08(){
employees.stream()
.filter((item)->item.getSalary()>=5000)
.limit(2)
.forEach(System.out::println);
}
运行效果:
2、Lambda表达式语法:
Java8中引入了一个新的操作符"->"该操作符称为箭头操作符或者是Lambda操作符
箭头操作符将Lambda拆分成两部分:
左侧:Lambda表达式的参数列表
右侧:Lambda表达式中所需要执行的功能,即Lambda 体
语法格式一:无参数,无返回值
()->System.out.println("Hello");
语法格式二:有一个参数,并且无返回值
(x)->System.out.println(x);
语法格式三:若只有一个参数,小括号可以省略不写,无返回值
x->System.out.println(x);
语法格式四:有两个以上的参数,并且Lambda体中有多条语句,并且有返回值
Comparator<Integer> com = (x,y) -> {
System.out.prinln("函数接口");
return Integer.compare(x,y);
};
语法格式五:若Lambda体中,只有一条语句的话,return和大括号都可以省略不写
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为Jvm编译器可以通过上下文推断出数据类型,即“类型推断”
语法总结:
左右遇一括号省
左侧推断类型省
回到上面的例子,比较两个字符串长短问题: first.length()-second.length() 转换为Lambda表达式即为:
//以下式子可以看成λfirst.λsecond.first.length()-second.length() (String first , String second)->first.length() - second.length()
如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在{}中,并包含显示的return语句。例如:
(String first,String second) ->{ if (first.length()<second.length()) return -1; else if (first.length()>second.length()) return -1; else return 0; }
即使lambda表达式没有参数,仍然要提供空括号,就如同无参方法一样: () -> {for(int i=100;i>=0;i--)System.out.println(i);}
3、函数式接口
Lambda表达式需要"函数式接口"的支持
什么是函数式接口呢:接口中只有一个抽象方法的接口,称为函数式接口。可以使用注解@FunctionalInterface修饰(检查是否是函数式接口)
接下来可能会有一个疑问:为什么函数式接口必须有一个抽象方法。不是接口中所有的方法都是抽象的吗?实际上,接口完全有可能重新声明Object类的方法, 如toString...,这些方法的声明可能会让方法不再是抽象的,而且,接口可以声明非抽象方法。
接下来我们有一个需求:对一个数进行运算
第一步:定义一个接口
@FunctionalInterface
public interface MyFunction {
public Integer getValue(Integer num);
}
第二步:测试
public class LambdaTest02 {
@Test
public void test01(){
Integer out = yunsuan(100, (item) -> item * item);
System.out.println(out);
}
public Integer yunsuan(Integer num,MyFunction myFunction){
return myFunction.getValue(num);
}
}