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中的forEach方法

供给型接口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;
}

三、注意事项


  1. lambda中使用局部变量必须要求该变量 显式声明为final或事实上的final ,这主要是因为局部变量存储在栈上,lambda表达式则在另一个线程中运行,当该线程视图访问该局部变量的时候,该变量存在被更改或回收的可能性,所以用final修饰之后就不会存在线程安全的问题。

四、总结


Lambda表达式其实并没有特别难的地方,现在看起来吃力是因为还没有习惯这种写法而已,代码执行的结果都是一样,目的就是为了节省代码量,并且提高代码的可读性,理解了lambda表达式后学习stream流操作或者看一些源码的时候才不会那么吃力,当然也可以先学习stream操作之后再来看lambda表达式(本人就是这样),你就会有一种恍然大悟的感觉。

函数式接口在实际的开发过程中很少去自己写,因为大部分你能想到的数据操作java都帮你写好了,如果对函数式接口还是不理解的话,推荐大家去java.util.function; 包下去看源码,会对理解函数式接口有很大的帮助,主要是理解函数式接口的思维,如何使用函数式接口?函数式接口的好处在哪?在实际的开发过程中,是否可以将函数式接口和具体的业务逻辑相结合?

posted @ 2020-05-29 09:53  ihyb  阅读(281)  评论(0)    收藏  举报