Java8新特性
Java8的新特性主要是lambda表达式和流,它是推动 Java 8 发布的最重要新特性,允许把函数作为一个方法的参数(函数作为参数传递进方法中)使用 Lambda 表达式可以使代码变的更加简洁紧凑
为什么要使用lambda
Lambda是一段可以传递的代码(能够做到将代码像数据一样进行传递)。使用Lambda表达式能够写出更加简洁、灵活的代码。并且,使用Lambda表达式能够使Java的语言表达能力得到提升。
为什么要学函数式编程
面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。现实世界中,数据和行为并存,程序也是如此,因此这两种编程方式我们都得学
和匿名内部类的比较
设计匿名内部类的目的,就是为了方便 Java 程序员将代码作为数据传递。不过,匿名内部类还是不够简便
入门示例:
button.addActionListener(event -> System.out.println("button clicked"));
说明:event 是参数名,-> 将参数和 Lambda 表达式的主体分开,在Lambda 表达式中无需指定参数类型
如何辨别Lambda表达式
简单示例:
1 Lambda 表达式不包含参数,使用空括号 () 表示没有参数。该 Lambda 表达式实现了 Runnable 接口,该接口也只有一个 run 方法,没有参数,且返回类型为 void
Runnable noArguments = () -> System.out.println("Hello World");
2 Lambda 表达式包含且只包含一个参数,可省略参数的括号
ActionListener oneArgument = event -> System.out.println("button clicked");
3 Lambda 表达式的主体不仅可以是一个表达式,而且也可以是一段代码块,使用大括号({})将代码块括起来,如下所示。该代码块和普通方法遵循的规则别无二致,可以用返回或抛出异常来退出。只有一行代码的 Lambda 表达式也可使用大括号,用以明确 Lambda表达式从何处开始、到哪里结束
Runnable multiStatement = () -> {
System.out.print("Hello");
System.out.println(" World");
};
4 Lambda 表达式也可以表示包含多个参数的方法,如下所示。这时就有必要思考怎样去阅读该 Lambda 表达式。这行代码并不是将两个数字相加,而是创建了一个函数,用来计算两个数字相加的结果。变量 add 的类型是BinaryOperator
BinaryOperator<Long> add = (x, y) -> x + y;
也可指定参数类型:
BinaryOperator
将 Lambda 表达式赋值给一个局部变量,或传递给一个方法作为参数,局部变量或方法参数的类型就是 Lambda 表达式的目标类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的函数接口是只有一个抽象方法的接口,用作 Lambda 表达式的类型
java中重要的函数接口:
第二部分:流(Stream API)
什么是流:流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算
作用:流使程序员得以站在更高的抽象层次上对集合进行操作,和
Iterator 类似,Stream 是一种内部迭代方式。将 Lambda 表达式和Stream 上的方法结合起来,可以完成很多常见的集合操作。
api文档地址:https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
Java 程序员在使用集合类时,一个通用的模式是在集合上进行迭代,然后处理返回的每一个元素
List接口中的stream()函数
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
Stream接口当中包含的方法:
- Stream接口中的方法的参数就是一个函数式接口
测试案例1:
@Test
public void stream01(){
List<Integer> list = new ArrayList<>();
list.add(1232);
list.add(435345);
list.add(657868);
list.stream().forEach(i -> System.out.println(i));
System.out.println(list.stream().count()); // count函数用
于输出集合中元素个数
}
打印结果:
1232
435345
657868
stream.filter一般适用于list集合,从集合中查询想要的数据
list.stream().forEach(System.out::println);
@Test
public void stream02(){
List<Integer> list = new ArrayList<>();
list.add(1232);
list.add(435345);
list.add(657868);
list.add(null);
list.stream().filter(StringUtils::isNotBlank).forEach(System.out::println); //将等于null的过滤出来再打印
list.stream().filter(i -> i>12344).forEach(j -> System.out.println(j));
}
这个意思是指:筛选出list集合中值大于12344的结果,然后将其打印出来
输出结果:
435345
657868
另外一种写法:
@Test
public void stream02(){
List<Integer> list = new ArrayList<>();
list.add(1232);
list.add(435345);
list.add(657868);
List<Integer> li = list.stream().filter(i -> i>12344).collect(Collectors.toList());
li.forEach(j -> System.out.println(j));
}
返回结果:
435345
657868
map流映射
map 操作就可以使用该函数,将一个流中的值转换成一个新的流。
@Test
public void map(){
List<String> list = new ArrayList<>();
list.add("sssasdf");
list.add("ahj");
list.add("kdf");
list.stream().map(r -> r.toUpperCase()).forEach(j -> System.out.println(j));
}
得到的结果:
SSSASDF
AHJ
KDF
对map的遍历:
@Test
public void map1(){
Map<String, String> map = new HashMap<>();
map.put("name", "啊啊");
map.put("age", "11");
map.forEach((k,v) -> {
if ("name".equals(k)) {
System.out.println(k);
}
});
}
输出结果:name
如果仅需打印的话:
map.forEach((k,v) -> System.out.println(k)); //打印K值
map.forEach((k,v) -> System.out.println(v)); //打印V值
方法引用
Java 8 为其提供了一个简写语法,叫作方法引用,帮助程序员重
用已有方法。用方法引用重写上面的 Lambda 表达式,代码如下:
Artist::getName
标准语法为 Classname::methodName。需要注意的是,虽然这是一个方法,但不需要在后面加括号,因为这里并不调用该方法。我们只是提供了和 Lambda 表达式等价的一种结构,在需要时才会调用。凡是使用 Lambda 表达式的地方,就可以使用方法引用。
如果你想使用 Lambda 表达式创建一个 Artist 对象,可能会写出如下代码:
(name, nationality) -> new Artist(name, nationality)
使用方法引用,上述代码可写为:
Artist::new
这段代码不仅比原来的代码短,而且更易阅读。Artist::new 立刻告诉程序员这是在创建一个 Artist 对象,程序员无需看完整行代码就能弄明白代码的意图。另一个要注意的地方是方法引用自动支持多个参数,前提是选对了正确的函数接口。
如果List的值为null,则使用list.stream会报错,如果list的size为0,就是里面没有任何元素,是可以正常使用stream
求一个List中的最大值:
@Test
public void demo(){
List<Integer> intList = new ArrayList<>();
intList.add(5);
intList.add(10);
intList.add(15);
System.out.println(intList.stream().max(Integer::compare).get());
}
打印结果:15
findAny()+ifPresent方法过滤集合中的单条结果(实用)
@Test
public void doDemo(){
List<User> list = new ArrayList();
User u1 = new User();
u1.setUsername("zhangsan");
list.add(u1);
User u2 = new User();
u2.setUsername("list");
list.add(u2);
User u3 = new User();
u3.setUsername(null);
u3.setAge(15);
list.add(u3);
User u4 = new User();
u4.setUsername("zhangsan");
list.add(u4);
list.stream().filter(r -> "con".equals(r.getUsername())).findAny().ifPresent(y->{
System.out.println(y.getAge());
});
}
未打印结果:
原因是用户名中没有匹配到名为"con"的记录,所以ifPresent中的内容没有执行
重点:
User uu = list.stream().filter(r -> "zhangsan".equals(r.getUsername())).findAny().orElse(u1);
System.out.println(uu.toString());
- orElse(T other) 不论容器是否为空,只要调用该方法, 则返回值就是u1
- orElseGet(Supplier<? extends T> supplier) 只有当容器为空时,才调用supplier.get()方法产生对象
User uu = list.stream().filter(r -> "zhangsan".equals(r.getUsername())).findAny().orElseGet(()->{
User user = new User();
return user;
});
这里需要注意orElseGet后面这里的lambda表达式的写法
关于Optional<T>
public String getCourse(Student stu) throws Exception {
return Optional.ofNullable(stu)
.map(s -> s.getTeacher())
.map(t -> t.getCourse())
.orElse("无课程");
}
这是利用Java 8 提供的Optional
如果不用的话,原始的写法,会有许多的if判断
public String getCourse(Student stu) throws Exception {//该方法根据学生获取其课程
if(stu != null){ // 这里必须写非空判断
Teacher t = stu.getTeacher();//获取学生的老师
if(t != null){ // 这里必须写非空判断
return t.getCourse();//返回老师的课程
}
}
return "无课程";
}
参考文章:https://blog.csdn.net/liudun_cool/article/details/116521317
相关参考文章链接:https://mp.weixin.qq.com/s/7l6FVTti9yKHHVUWIyemtA
github上关于lambda的练习案例:
https://github.com/RichardWarburton/java-8-lambdas-exercises
评论区