不知道面试会不会问Lambda怎么用(推荐)

我们先假设一个场景想象一下,当一个项目出现bug的时候,恰巧这个时候需要你去修改,而当你打开项目之后,眼前的代码让你有一种特别严重的陌生感,你会不会慌?心里是不是瞬间就会喷涌而出各种想法:我这是打开的啥语言的项目?还是我眼花看错了?难道是我过时了?这写的是个啥子玩意儿…

java8在14年就出来了,已经很久了,但是还是有很多人没用过,包括我之前的同事都对这个不太熟悉,原因可能是多样的,可能是老程序员觉得没必要;也可能是性格使然,拒绝接受新的东西,一切守旧,能用就行;也可能是项目太老了,还在用JDK1.7,或者更老的版本,平时根本就接触不到java8的写法,也不需要去接触。

无论是什么原因,在新事物出现之后,没有一股探险精神,不去尝试,不去结合自己的处境去思考,这样下去就算天上掉馅饼也轮不到你啊。

这篇短文说下Lambda表达式,有一定的编程基础的小伙伴简单看下应该就会明白,不仅仅写着舒服,更能提供你的工作效率,让你有更多的时间带薪划水,自我提高,走向人生巅峰。

Lambda表达式

Lambda表达式可以理解为一种匿名函数:没有名称、有参数列表、函数主体、返回类型,可能还会有异常的列表。

参数 -> 主体

lambda表达式:(parameters) -> expression 或者是 (parameters) -> { statements; }

函数式接口

什么是函数式接口?

仅仅定义了一个抽象方法的接口,类似于Predicate、Comparator和Runnable。

@FunctionalInterface 函数式接口都带有这个注解,这个注解表示这个接口会被设计为函数式接口。

行为参数化

一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。

函数式接口可以做些什么?

Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并且把整个表达式作为函数式接口的实例,也就是说,Lambda是函数式接口的一个具体实现。函数式接口和Lambda会在项目中写出更加简洁易懂的代码。

接下来我们看下几种函数式接口:

  1. java.util.function.Predicate:这个接口中定义了一个test的抽象方法,它接受泛型T对象,并返回一个boolean值,在你需要表示一个涉及类型T的布尔表达式时,就可以使用这个接口。
  2. java.util.function.Consumer:这个接口中定义了accept抽象方法,它接受泛型T的对象,没有返回。如果你需要访问类型T的对象,并执行某些操作,可以用它。
  3. java.util.function.Function:这个接口定义了一个apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象,如果你需要定一个Lambda,将输入对象的信息映射到输出对象,就可以使用这个接口。
  4. ps:我们也可以自己定义一个自己需要的函数式接口。

这么说实在是太生涩了,还是贴点代码,让大家都看看:

@FunctionalInterface
public interface Predicate<T> {
 //我只截取了部分代码,test是这个接口唯一的抽象方法,话说从java8开始,接口中不仅
 //仅只能有抽象方法了,实现的方法也可以存在,用default和static来修饰。
 boolean test(T t);
 default Predicate<T> and(Predicate<? super T> other) {
  Objects.requireNonNull(other);
  return (t) -> test(t) && other.test(t);
 }

。

接下来,看下Lambda和函数式接口是怎么配合,一起快乐的工作的: 首先定义一个方法,这个方法的参数中有函数式接口:

private static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
 List<T> result = new ArrayList<>();
 for (T e : list) {
  if (predicate.test(e)) {
   result.add(e);
  }
 }
 return result;
}

接下来你就可以这么写:

List<Apple> apples = filter(list, (Apple apple) -> "red".equals(apple.getColor()));

以上,filter方法的参数是一个泛型集合和Predicate,这个函数式接口中的抽象方法是接受一个对象并返回一个布尔值,所以Lambda我们可以写成参数是一个实体对象Apple,主体是一个返回boolean值的表达式,将这段Lambda作为参数传给filter()方法,这也是java8的行为参数化特性。以上我们就可以挑选出红苹果。

使用了泛型,就代表着我们还可以复用这段代码做些别的事情,挑选出你想要东东的:

List<String> stringList = filter(strList, StringUtils::isNoneBlank);

抽象方法的方法签名和Lambda表达式的签名是一一对应的,如果你要应用不同的Lambda表达式,就需要多个函数式接口,当然了我也是可以自己定义的。

在java中只有引用类型或者是原始类型,这是由泛型内部的实现方式造成的。因此,在Java里有一个将原始类型转换为对应的引用类型的机制,这个机制叫作装箱(boxing)。相反的操作,也就是将引用类型转换为对应的原始类型,叫作拆箱(unboxing)。

Java还有一个自动装箱机制,也就是说装箱和拆箱操作是自动完成的,但这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存来搜索获取被包裹的原始值。

针对于这一点,java8中的函数式接口提供了单独的接口,就是为了在输入和输出的时候避免自动装箱拆箱的操作,是不是很贴心。

一般情况下,在名称上我们就能看得出来,一目了然。在原来的名称上会有原始类型前缀。像Function接口针对输出参数类型的变形。比如说:ToIntFunction、IntToDoubleFunction等。

在必要的情况下,我们也可以自己定义一个函数式接口,请记住,(T,U) -> R的表达方式展示了对一个函数的简单描述,箭头的的左侧代表了参数类型,右侧代表着返回类型,这里它代表一个函数,具有两个参数,分别为泛型T和U,返回类型为R。

函数式接口是不允许抛出 受检异常(checked exception),但是有两个方法可以抛出异常:

  1. 定义一个自己的函数式接口,在唯一的抽象方法抛出异常;
  2. 用try-catch 将lambda 包起来。

类型检查

java7是通过泛型从上下文推断类型,lambda的类型检查是通过它的上下文推断出来的。lambda会找到它所在的方法的方法签名,也就是它的参数,也就是他们说的目标类型,再找到这个方法中定义的抽象方法,这个方法描述的函数描述符是什么?也就是这个方法是个什么样的,接受什么参数,返回什么。lambda也必须是符合这样的。当lambda抛出异常的时候,那个抽象方法也必须要抛出异常。
有了目标类型,那么同一个lambda就可以与不同的函数式接口联系起来。只要他们的抽象方法签名是一样的。
例如:

Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> p = () -> 42;

这两个接口都是没有参数,且返回一个泛型T的函数。 void兼容规则 lambda的主题是一个语句表达式,和一个返回void的函数描述符兼容,包括参数列表, 比如下面:

// Predicate返回了一个boolean
Predicate<String> p = s -> list.add(s);
// Consumer返回了一个void
Consumer<String> b = s -> list.add(s);

在lambda中使用局部变量

final int local_value = 44;
Consumer<String> stringConsumer = (String s) -> {
   int new_local_value = s.length() + local_value;
  };

在lambda中可以无限制的使用实例变量和静态变量,但是只能是final的,如果在表达式里面给变量赋值,就会编译不通过。为什么会有这样的呢?

因为实例变量存储在堆中,局部变量存储在栈中,lambda是在一个线程中,如果lambda可以直接访问局部变量,lambda的线程可能会在分配该变量的线程将这个变量回收之后,再去访问该变量。在访问局部变量的时候,实际上是访问他的副本,而不是原始变量。

方法引用

方法引用,方法目标实体放在::的前面,方法名放在后面。比如 Apple::getWeight,不需要括号。

构造函数是可以利用它的名称和关键字 new来创建一个引用。

//Supplier也是一个函数式接口,唯一的抽象方法不接受参数,直接返回一个对象
Supplier<Apple> sup = Apple::new;
  Apple apple = sup.get();

但是如果是有参数的呢?

//一个参数
Function<Long, Apple> fun = Apple::new;
    Apple apple1 = fun.apply(110L);
//两个参数
 BiFunction<Long, String, Apple> biFunction = Apple::new;
 Apple biApple = biFunction.apply(3L, "red");

但是如果有三个参数、四个参数呢?我们上面说了怎么样可以自定义一个自己需要的函数式接口。

@FunctionalInterface
public interface AppleWithParam<T, U, V, R> {
  R apply(T t, U u, V v);
}

总结:

  1. java8中自带的函数式接口,以及为了避免拆装箱操作而产生的函数式接口的原始类型转化。
  2. 函数式接口就是仅仅定义一个抽象方法的接口。抽象方法的签名(称为函数描述符) 描述了Lambda表达式的签名。
  3. 只有在接受函数式接口的地方才可以使用Lambda表达式。
  4. 接口现在还可以拥有默认方法,(就是类没有对方法进行实现的时候,它实现的接口来提供默认实现的方法)

以上所述是小编给大家介绍的Lambda表达式详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Java8新特性Lambda表达式的一些复杂用法总结

    简介 lambda表达式是JAVA8中提供的一种新的特性,它支持Java也能进行简单的"函数式编程". 它是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数. 本文将介绍关于Java8 Lambda表达式的一些复杂用法,分享出来供大家参考学习,下面来一起看看详细的介绍: 复杂用法实例 传入数组ids,在list<Obj>上操作,找出Obj中id想匹配的,并且按

  • Java8中的 Lambda表达式教程

     1. 什么是λ表达式 λ表达式本质上是一个匿名方法.让我们来看下面这个例子: public int add(int x, int y) { return x + y; } 转成λ表达式后是这个样子: (int x, int y) -> x + y; 参数类型也可以省略,Java编译器会根据上下文推断出来: (x, y) -> x + y; //返回两数之和 或者 (x, y) -> { return x + y; } //显式指明返回值 可见λ表达式有三部分组成:参数列表,箭头(-&g

  • Java8 Lambda表达式详解及实例

    第一个Lambda表达式 在Lambda出现之前,如果我们需要写一个多线程可能需要下面这种方式: Runnable runnable = new Runnable() { @Override public void run() { System.out.println("Hello runnable"); } }; ... thread.start(); 上面的例子如果改成使用Lambda就会简单许多: Runnable noArgs = ()->System.out.print

  • Java8中的lambda表达式入门教程

    1.基本介绍 lambda表达式,即带有参数的表达式,为了更清晰地理解lambda表达式,先上代码: 1.1 两种方式的对比 1.1.1 方式1-匿名内部类 class Student{ private String name; private Double score; public Student(String name, Double score) { this.name = name; this.score = score; } public String getName() { ret

  • Java8新特性之Lambda表达式浅析

    说到java 8,首先会想到lambda(闭包)以及虚拟扩展方法(default method),这个特性早已经被各大技术网站炒得沸沸扬扬了,也是我们java 8系列开篇要讲的第一特性(JEP126 http://openjdk.java.net/jeps/126),jdk8的一些库已经应用了lambda表达式重新设计了,理解他对学习java 8新特性有着重要的意义. 一.函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是

  • Java8 新特性Lambda表达式实例详解

    Java8 新特性Lambda表达式实例详解 在介绍Lambda表达式之前,我们先来看只有单个方法的Interface(通常我们称之为回调接口): public interface OnClickListener { void onClick(View v); } 我们是这样使用它的: button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { v.setText("

  • Java8深入学习系列(一)lambda表达式介绍

    前言 最近在学习java8,所以接下来会给大家介绍一系列的Java8学习内容,那么让我们先从lambda表达式开始. 众所周知从java8出现以来lambda是最重要的特性之一,它可以让我们用简洁流畅的代码完成一个功能. 很长一段时间java被吐槽是冗余和缺乏函数式编程能力的语言,随着函数式编程的流行java8种也引入了 这种编程风格.在此之前我们都在写匿名内部类干这些事,但有时候这不是好的做法,本文中将介绍和使用lambda, 带你体验函数式编程的魔力. 什么是lambda? lambda表达

  • Java8中lambda表达式的应用及一些泛型相关知识

    语法部分就不写了,我们直接抛出一个实际问题,看看java8的这些新特性究竟能给我们带来哪些便利 顺带用到一些泛型编程,一切都是为了简化代码 场景: 一个数据类,用于记录职工信息 public class Employee { public String name; public int age; public char sex; public String time; public int salary; } 我们有一列此类数据 List<Employee> data = Arrays.asL

  • 浅析Java8新特性Lambda表达式和函数式接口

    什么是Lambda表达式,java8为什么使用Lambda表达式? "Lambda 表达式"(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数.我们可以把 Lambda表达式理解为是 一段可以传递的代码.最直观的是使用Lambda表达式之后不用再写大量的匿名内部类,简化代码,提高了代码的可读性. // 启动一个线程,不使用Lambda

  • Java8新特性lambda表达式有什么用(用法实例)

    我们期待了很久lambda为java带来闭包的概念,但是如果我们不在集合中使用它的话,就损失了很大价值.现有接口迁移成为lambda风格的问题已经通过default methods解决了,在这篇文章将深入解析Java集合里面的批量数据操作(bulk operation),解开lambda最强作用的神秘面纱. 1.关于JSR335 JSR是Java Specification Requests的缩写,意思是Java 规范请求,Java 8 版本的主要改进是 Lambda 项目(JSR 335),其

随机推荐