java8新特性一、Lambda表达式
Lambda表达式
截止到现在做java开发也有了大半年了,虽然一直使用stream流,lambda表达式,函数式接口和方法引用等,但是并没有深入了解过,所以趁着这个机会跟大家交流交流,如果有不对的或者需要补充的地方请大家及时提出。
lambda表达式,通俗一点来说,就是把函数作为参数传递进方法中,这样做的意义是为了减少代码量,让代码变的更加简洁。
一、Lambda表达式
我们可以将lambda表达式看作一个匿名函数,首先必须明确lambda表达式从本质上说是一个函数,所以它具备了参数列表、函数主体、返回类型、甚至可以抛出异常;还有一点,它是匿名的,所以lambda表达式没有具体的函数名称;其格式定义如下:
(参数列表) -> 表达式
(参数列表) ->
下面是lambda表达式的简单例子
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 3. 返回给定字符串的长度
(String s) -> s.length()
// 4. 包含多行表达式,需用花括号括起来,并显示添加return (x + x * y)
(int x, int y) -> {
int z = x * y;
return x + z;
}
二、函数式接口
依托于函数式接口使用lambda表达式
函数式接口(Functional Interface)是Java 8中对一类特殊的接口的称呼。这类接口只定义了唯一的抽象方法(有且仅有一个抽象方法,但是可以有多个非抽象方法的接口),并且这类接口使用了
@FunctionalInterface进行注解。
这些函数式接口一般是不需要我们去写的,在jdk8中,引入了一个新的包 java.util.function; (看一下这些源码,有助于更好的理解函数式接口),这些接口都在包里,最常用最具有代表性的有四个类:
| 类 | 注释 | 抽象方法 | 说明 |
|---|---|---|---|
| Consumer < T > | 消费型接口 | void accept(T t); | 有参数,无返回值。执行在单个参数上面的操作 |
| Supplier < T > | 供给型接口 | T get(); | 无参数,有返回值。 |
| Function <T, R> | 函数型接口 | R apply(T t); | 有参数,有返回值。传入T,返回R |
| Predicate < T > | 段言型接口 | boolean test(T t); | 有参数,有返回值(返回值为boolean)。用于判断操作 |
消费型接口Consumer < T >
Consumer提供了一个accept抽象函数,该函数接收参数,但不返回值,下面利用Consumer遍历集合:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
default关键字可以让接口中的方法可以有默认的函数体,当一个类实现这个接口时,可以不用去实现这个方法
例子:
public static void main(String[] args) {
List<String> list = Arrays.asList("qwer","df","zxcv","asdfghjkl");
// 非lambda写法
forStr(list, new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
// lambda写法
forStr(list, s -> System.out.println(s));
// 方法引用(采用方法引用可以更近一步的简化代码,有时候这种简化让代码看上去更加的直观)
forStr(list, System.out::println);
}
// 需求:将字符串遍历
public static void forStr(List<String> list, Consumer<String> consumer){
for (String str : list) {
consumer.accept(str);
}
}
源码参照: interface Iterable

供给型接口Supplier < T >
Supplier接口一般用作实例化一个空对象或者调用对象的get()方法等场景。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
例子:
public static void main(String[] args) {
String str1 = "qwer";
String str2 = "tyuiop";
// 非lambda写法
String str4 = getStr(new Supplier<String>() {
@Override
public String get() {
return str1 + str2;
}
});
System.out.println(str4);
// lambda写法
String str3 = getStr(()->str1+str2);
System.out.println(str3);
}
// 需求:将字符串返回
public static String getStr(Supplier<String> supplier){
return supplier.get();
}
函数型接口 Function <T, R>
Funcation执行转换操作,输入是类型T的数据,返回R类型的数据,下面利用Function对集合进行转换:
public interface Function<T, R> {
R apply(T t);
}
例子:
public static void main(String[] args) {
List<String> list = Arrays.asList("11","12","13","15");
// 非lambda写法
List<Integer> listInteger = convert(list, new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
});
// lambda写法
listInteger = new ArrayList<Integer>();
listInteger = convert(list, (s) -> Integer.parseInt(s));
// 方法引用
listInteger = new ArrayList<Integer>();
listInteger = convert(list, Integer::parseInt);
}
// 需求:将字符串转换为数字
public static List<Integer> convert(List<String> list, Function<String, Integer> function) {
List<Integer> newList = new ArrayList<Integer>();
for (final String t : list) {
newList.add(function.apply(t));
}
return newList;
}
段言型接口 Predicate < T >
Predicate利用我们在外部设定的条件对于传入的参数进行校验,并返回验证结果boolean,下面利用Predicate对List集合的元素进行过滤:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
例子:
public static void main(String[] args) {
List<String> list = Arrays.asList("qwer","df","zxcv","asdfghjkl");
// 非lambda写法
Predicate predicate = new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length()>3;
}
};
List<String> filterList = filterStr(list,predicate);
System.out.println(filterList);
filterList = new ArrayList<String>();
// lambda写法
filterList = filterStr(list, s -> s.length()>3);
System.out.println(filterList);
}
// 需求:将满足条件的字符串放到集合中
public static List<String> filterStr(List<String> list, Predicate<String> predicate){
List<String> strlist = new ArrayList<>();
for(String str:list){
if(predicate.test(str)){
strlist.add(str);
}
}
return strlist;
}
三、注意事项
- lambda中使用局部变量必须要求该变量 显式声明为final或事实上的final ,这主要是因为局部变量存储在栈上,lambda表达式则在另一个线程中运行,当该线程视图访问该局部变量的时候,该变量存在被更改或回收的可能性,所以用final修饰之后就不会存在线程安全的问题。
四、总结
Lambda表达式其实并没有特别难的地方,现在看起来吃力是因为还没有习惯这种写法而已,代码执行的结果都是一样,目的就是为了节省代码量,并且提高代码的可读性,理解了lambda表达式后学习stream流操作或者看一些源码的时候才不会那么吃力,当然也可以先学习stream操作之后再来看lambda表达式(本人就是这样),你就会有一种恍然大悟的感觉。
函数式接口在实际的开发过程中很少去自己写,因为大部分你能想到的数据操作java都帮你写好了,如果对函数式接口还是不理解的话,推荐大家去java.util.function; 包下去看源码,会对理解函数式接口有很大的帮助,主要是理解函数式接口的思维,如何使用函数式接口?函数式接口的好处在哪?在实际的开发过程中,是否可以将函数式接口和具体的业务逻辑相结合?

浙公网安备 33010602011771号