JAVA8新特性一:lambda表达式

大家好,最近入职了新的公司,所以很久都没有写新博客了(其实就是懒)
新公司大量的使用了一些JAVA8的新特性,所以也恶补了一下JAVA8的知识(好吧我知道JAVA14都出来了我还在写JAVA8的特性确实有点落伍了)但最近看了一下实战系列的JAVA8实战,感觉很受启发,作者的高度带给了我不一样的角度来看这些特性。

Lambda表达式

Lambda表达式概述

首先让我们来康康JAVA8最大的一个变动,支持了Lambda表达式。其实这个变动也是JAVA不得已而为之,因为他的竞争对手都支持了这种方便的写法,不思进取的话只能落得和cobol前辈一样的下场。
用最简单的话说,Lambda其实就是把一段代码作为了一个变量,在JAVA8之前变量只能是那几大基础类型+引用类型,而把一段代码作为变量有什么好处呢?好处是可以很简洁的描述匿名内部类,让我们来看一个《JAVA8实战》中的例子:

假设你在设计一个农场库存程序,你必须实现一个从列表中筛选绿苹果的功能。

好,是不是很简单?让我们来实现它

第一版:单纯的绿苹果筛选
public static List<Apple> filterGreenApples(List<Apple> inventory) { List<Apple> result = new ArrayList<Apple>();
for(Apple apple: inventory){
  if( "green".equals(apple.getColor() ) { result.add(apple);
} }
    return result;
}

OK,非常简单,遍历List逐个比较颜色,把符合条件的放进新List。
现在农民伯伯提出需求:
他还想要筛选红苹果。 你该怎么做呢?

第二版:抽象颜色
public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) {
    List<Apple> result = new ArrayList<Apple>();
    for (Apple apple: inventory){
if ( apple.getColor().equals(color) ) { result.add(apple);
} }
    return result;
}

我们多加了个参数叫color,用以抽象颜色,这样农民伯伯无论要什么颜色的苹果,都可以筛选。

但是,农民伯伯又来了:“要是能区分 轻的苹果和重的苹果就太好了。重的苹果一般是重量大于150克。”你握紧了拳头,对农民伯伯抱拳以示尊敬。马上坐下来又开始编写:

第三版:单纯的重量筛选

public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
        List<Apple> result = new ArrayList<Apple>();
        For (Apple apple: inventory){
if ( apple.getWeight() > weight ){ result.add(apple);
} }
        return result;
    }

OK,还是很简单。但你已经开始注意到事情没对了,水温逐渐升高,作为一只老练的青蛙你发现你写了一个很类似颜色筛选的方法,单纯只是从比较颜色变成了比较重量。

第四版:重量或颜色筛选(通过flag来控制)
public static List<Apple> filterApples(List<Apple> inventory, String color, int weight, boolean flag) {
     List<Apple> result = new ArrayList<Apple>();
    for (Apple apple: inventory){
if ( (flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight) ){ result.add(apple);
} }
    return result;
}

你通过一个flag来表示是要比较颜色还是比较重量,这样这个方法可以筛选颜色或重量,but。。。你觉不觉得那个flag很丑?再说,如果农民伯伯又跑过来说我还需要筛选形状和大小,难道你需要加更多的flag来控制到底筛选哪个吗?

第五版:策略模式
//重量筛选器
public class AppleHeavyWeightPredicate implements ApplePredicate{ 
	public boolean test(Apple apple){
	return apple.getWeight() > 150; 
	}
}
//颜色筛选器
public class AppleGreenColorPredicate implements ApplePredicate{
	public boolean test(Apple apple){
	return "green".equals(apple.getColor());
 	 } 
}
//我们要比什么,就放什么筛选器进去
public static List<Apple> filterApples(List<Apple> inventory,
                                           ApplePredicate p){
        List<Apple> result = new ArrayList<>();
        for(Apple apple: inventory){
			if(p.test(apple)){ result.add(apple);
			} 
		}
    return result;
}

好了,到这一步策略模式也用上了,感觉越来越高级了哈。之后如果要再筛选什么,我们直接写一些筛选器就好了,看起来非常好。
但能不能再改进呢?好像也不是不行,如果硬要挑骨头的话,我们会觉得每次要新建好多筛选器类啊,如果有的筛选器类就用得到一次,确实在我们工程里很不妥,删又删不得,但又没啥其他的作用。
其实我们不用特地声明筛选器啊,在要筛选的时候,用一个匿名类不就好了吗?

第六版:匿名类
List<Apple> redApples = filterApples(inventory, new ApplePredicate() { 
public boolean test(Apple apple){
	return "red".equals(apple.getColor());
	}
});

Good,这样我们就不用新建很多筛选器类了,但尴尬的是代码好像没有减少啊。。。代码只是从一个单独的类移到了方法入参。。。。

这时候就该。。。。。Lambda救场!

第七版:Lambda
List<Apple> result =
filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));

仔细看,Lambda用(Apple apple) -> “red”.equals(apple.getColor())取代了匿名类的声明,这就是它的简洁之处。

当然,现在可能你还不知道这行带箭头的代码是什么意思,但没事,我们已经一步一步引出了Lambda的必要性与优越性。

Lambda语法初探

我们以一个例子来讲解Lambda
在这里插入图片描述
Lambda分三部分:

参数列表——这里它采用了Comparator中compare方法的参数,两个Apple。
箭头——箭头->把参数列表与Lambda主体分隔开。
Lambda主体——比较两个Apple的重量。表达式就是Lambda的返回值了。

大概知道了这三部分后,我将引用一篇知乎的文章来讲解Lambda,当时我看了如醍醐灌顶,知乎链接在这里,也可以直接去看:
Lambda 表达式有何用处?如何使用? - Mingqi的回答 - 知乎
知乎地址

我们知道,对于一个Java变量,我们可以赋给其一个“值”。如果你想把“一块代码”赋给一个Java变量,应该怎么做呢?比如,我想把右边那块代码,赋给一个叫做aBlockOfCode的Java变量:在Java 8之前,这个是做不到的。但是Java 8问世之后,利用Lambda特性,就可以做到了。当然,这个并不是一个很简洁的写法。所以,为了使这个赋值操作更加elegant, 我们可以移除一些没用的声明。这样,我们就成功的非常优雅的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个Lambda表达式。但是这里仍然有一个问题,就是变量aBlockOfCode的类型应该是什么?在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。直接这样说可能还是有点让人困扰,我们继续看看例子。我们给上面的aBlockOfCode加上一个类型:这种只有一个接口函数需要被实现的接口类型,我们叫它”函数式接口“。为了避免后来的人在这个接口中增加接口函数导致其有多个接口函数需要被实现,变成"非函数接口”,我们可以在这个上面加上一个声明@FunctionalInterface, 这样别人就无法在里面添加新的接口函数了。这样,我们就得到了一个完整的Lambda表达式声明:

---------------引用完毕
所以lambda里代表的方法,就是函数式接口里的那个唯一的方法。
当然,你可能会想:“为什么只有在需要函数式接口的时候才可以传递Lambda呢?”语言的设计者 也考虑过其他办法,例如给Java添加函数类型(有点儿像我们介绍的描述Lambda表达式签名的特 殊表示法)。但是他们选择了现在这种方式,因为这种方式自然且能避免语言变得更复杂。

JAVA自带的函数式接口

Lambda必须要有函数式接口才能用,JAVA设计师也设计了几个内嵌的函数式接口供我们使用:
Predicate
java.util.function.Predicate接口定义了一个名叫test的抽象方法,它接受泛型 T对象,并返回一个boolean。

consumer
java.util.function.Consumer定义了一个名叫accept的抽象方法,它接受泛型T 的对象,没有返回(void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用 这个接口。

Function
java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个 泛型T的对象,并返回一个泛型R的对象。如果你需要定义一个Lambda,将输入对象的信息映射 到输出,就可以使用这个接口(比如提取苹果的重量,或把字符串映射为它的长度)。

局部变量的限制

当我们在Lambda里使用局部变量时,一定要注意必须加上final修饰符的局部变量才可以在Lambda中使用(当然大多数情况其实也不会使用到局部变量)

原因如下:
第一,实例变量和局部变量背后的实现有一 个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局 部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线 程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问它 的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了 这个限制。

第二,这一限制不鼓励你使用改变外部变量的典型命令式编程模式

  • 11
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Java 8引入了lambda表达式作为一种新的编程语言特性。lambda表达式是一种匿名函数,它可以作为参数传递给方法或存储在变量。它可以简化代码,使代码更加易读和易维护。lambda表达式语法非常简洁,可以用来替代匿名内部类。它可以在集合框架使用,使代码更加简洁和易读。lambda表达式Java 8最重要的新特性之一,它使Java编程更加现代化和高效。 ### 回答2: Lambda 表达式是 Java 8 最重要的新增特性之一,它可以让我们以更简洁的方式来编写代码,并且能够更优雅的解决许多问题。 Lambda 表达式本身是一个匿名函数,它可以被当做对象进行传递和处理。它强调的是函数式编程思想,将函数作为一等公民,并支持灵活的函数组合。Lambda 表达式通常由左侧的参数列表、箭头符号和右侧的函数体组成。例如: ``` (x, y) -> x + y ``` 上述代码定义了一个 lambda 表达式,这个表达式接受两个参数 x 和 y,然后返回它们的和。 Lambda 表达式的用途非常广泛,它们通常用于简化代码,比如用于普通的遍历集合和数组: ``` List<String> names = Arrays.asList("alice", "bob", "charlie"); names.forEach(name -> System.out.println(name)); ``` Lambda 表达式还可以用于函数式接口,这是一种只包含一个抽象方法的接口。函数式接口可以被当做 lambda 表达式的类型,这意味着我们可以使用 lambda 表达式来创建这种类型的对象。例如: ``` interface Calculator { int calculate(int a, int b); } Calculator add = (a, b) -> a + b; Calculator sub = (a, b) -> a - b; ``` 上述代码定义了一个接口类型 Calculator,它包含一个抽象方法 calculate,并且使用 lambda 表达式来定义 add 和 sub 两个对象,它们分别代表加法和减法。 Lambda 表达式还支持方法引用,这是一种更简洁的语法形式,它允许我们使用方法名来代替 lambda 表达式的函数体。例如: ``` List<String> names = Arrays.asList("alice", "bob", "charlie"); names.forEach(System.out::println); ``` 上述代码使用了方法引用,它代替了之前的 lambda 表达式。 总之,Java 8 的 Lambda 表达式是一项非常有用的功能,它提供了一种更简单的方式来编写代码,让我们的代码更加简约且易于阅读。同时,它也是一种函数式编程思想的体现,让 Java 开发者能够更好的应用这些思想来解决实际问题。 ### 回答3: Java8的lambda表达式是一个Java编程语言的新特性,它可以方便地为函数式接口创建实例。Lambda表达式允许你直接以更加简单和精简的方式来定义内部类,并且能够省略掉那些没有用的代码。 在Java 8Lambda表达式通过一个箭头“->”来定义。在箭头左边是参数列表,这些参数可以是任何类型,但是Lambda表达式只能有一个方法参数。在箭头右边是Lambda表达式的主体,也就是这个Lambda表达式所要执行的代码块。 Lambda表达式还可以访问外部作用域的变量,这是通过捕获变量的方式来实现的。Lambda表达式在使用外部变量时会将其捕获到Lambda表达式内部,从而形成一个闭包,这使得Lambda表达式能够访问外部的变量。 Lambda表达式的使用能够使得代码更加简洁,能够将代码逻辑更加清晰地表达。在Java8Lambda表达式被广泛应用于集合的处理,比如通过对集合进行排序、过滤、映射等操作,能够更加简单地处理数据集合。Lambda表达式还被应用于多线程的编程,能够使得并发编程更加方便和简单。 总之,Java 8的Lambda表达式是一个很有用的新特性。它能够使得Java代码更加简洁和易于理解,减少了冗余的结构和语法。同时,它也提供了更加灵活的编程方式,使得Java编程能够更加高效和便利。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值