1. 几个例子
场景:现在要对一些苹果进行分类,可以按颜色分,也可以按重量分 一种实现如下:
package test;
import java.util.ArrayList;
import java.util.List;
class Apple{
String color;
double weight;
public Apple(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
@Override
public String toString() {
return "Apple [color=" + color + ", weight=" + weight + "]";
}
}
interface FilterApple{
public boolean test(Apple apple);
}
class FilterAppleByColor implements FilterApple{
@Override
public boolean test(Apple apple) {
return apple.color.equals("red");
}
}
class FilterAppleByWeight implements FilterApple{
@Override
public boolean test(Apple apple) {
return apple.weight>50;
}
}
public class LambdaDemo {
public static void filterApple(List<Apple> apples,FilterApple filter) {
for(Apple apple:apples)
{
if(filter.test(apple))
System.out.println(apple);
}
}
public static void main(String[] args) {
Apple apple1=new Apple("red",50);
Apple apple2=new Apple("red",100);
Apple apple3=new Apple("green",50);
Apple apple4=new Apple("green",100);
List<Apple> apples=new ArrayList<>();
apples.add(apple1);
apples.add(apple2);
apples.add(apple3);
apples.add(apple4);
System.out.println("红色苹果:");
FilterApple fByColor=new FilterAppleByColor();
filterApple(apples, fByColor);
System.out.println("重量>50g的苹果");
FilterApple fByWeight=new FilterAppleByWeight();
filterApple(apples, fByWeight);
}
}
当然也可以使用匿名类实现:
package test;
import java.util.ArrayList;
import java.util.List;
class Apple{
String color;
double weight;
public Apple(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
@Override
public String toString() {
return "Apple [color=" + color + ", weight=" + weight + "]";
}
}
interface FilterApple{
public boolean test(Apple apple);
}
public class LambdaDemo {
public static void filterApple(List<Apple> apples,FilterApple filter) {
for(Apple apple:apples)
{
if(filter.test(apple))
System.out.println(apple);
}
}
public static void main(String[] args) {
Apple apple1=new Apple("red",50);
Apple apple2=new Apple("red",100);
Apple apple3=new Apple("green",50);
Apple apple4=new Apple("green",100);
List<Apple> apples=new ArrayList<>();
apples.add(apple1);
apples.add(apple2);
apples.add(apple3);
apples.add(apple4);
System.out.println("红色苹果:");
filterApple(apples, new FilterApple() {
@Override
public boolean test(Apple apple) {
return apple.color.equals("red");
}
});
System.out.println("重量>50g的苹果");
filterApple(apples, new FilterApple() {
@Override
public boolean test(Apple apple) {
return apple.weight>50;
}
});
}
}
上面两个代码的问题在于,代码冗余量较大 第一份代码中: 根据颜色分类的话要实现一个接口,根据重量分类的话也要实现一个接口 第二份代码中:虽然使用了匿名类,本质上还是实现了接口,假设需要多次按颜色分类,就会产生大量的重复代码
Lambda表达式改进
package test;
import java.util.ArrayList;
import java.util.List;
class Apple{
String color;
double weight;
public Apple(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
@Override
public String toString() {
return "Apple [color=" + color + ", weight=" + weight + "]";
}
}
interface FilterApple{
public boolean test(Apple apple);
}
public class LambdaDemo {
public static void filterApple(List<Apple> apples,FilterApple filter) {
for(Apple apple:apples)
{
if(filter.test(apple))
System.out.println(apple);
}
}
public static void main(String[] args) {
Apple apple1=new Apple("red",50);
Apple apple2=new Apple("red",100);
Apple apple3=new Apple("green",50);
Apple apple4=new Apple("green",100);
List<Apple> apples=new ArrayList<>();
apples.add(apple1);
apples.add(apple2);
apples.add(apple3);
apples.add(apple4);
System.out.println("红色苹果:");
filterApple(apples, (Apple a)->a.color.equals("red"));
System.out.println("重量>50g的苹果");
filterApple(apples, (Apple a)->a.weight>50);
}
}
上面的代码将匿名类换成了Lambda表达式
2. Lambda表达式概念
可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式
几个特点:
- 匿名:不像普通的方法那样有一个明确的名称:写得少而想得多!
- 函数:Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表
- 传递:Lambda表达式可以作为参数传递给方法或存储在变量中
- 简洁:无需像匿名类那样写很多模板代码
再使用一个例子熟悉Lambda表达式,现在需要按重量对苹果进行排序,Collectios.sort(collectios,comparator) 这个方法可以进行排序,可以传入一个Comparator 根据我们的需要按自定义类的某个属性排序
package test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Apple{
String color;
double weight;
public Apple(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
@Override
public String toString() {
return "Apple [color=" + color + ", weight=" + weight + "]";
}
}
interface FilterApple{
public boolean test(Apple apple);
}
public class LambdaDemo {
public static void filterApple(List<Apple> apples,FilterApple filter) {
for(Apple apple:apples)
{
if(filter.test(apple))
System.out.println(apple);
}
}
public static void main(String[] args) {
Apple apple1=new Apple("red",50);
Apple apple2=new Apple("red",25);
Apple apple3=new Apple("green",75);
Apple apple4=new Apple("green",100);
List<Apple> apples=new ArrayList<>();
apples.add(apple1);
apples.add(apple2);
apples.add(apple3);
apples.add(apple4);
Comparator<Apple> comparator=new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
if(o1.weight>o2.weight)
return 1;
else if(o1.weight<o2.weight){
return -1;
}else {
return 0;
}
}
};
System.out.println("排序前:");
for(Apple apple:apples)
System.out.println(apple);
Collections.sort(apples,comparator);
System.out.println("排序后:");
for(Apple apple:apples)
System.out.println(apple);
}
}
下面改用匿名类实现Comparator接口
package test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Apple{
String color;
double weight;
public Apple(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
@Override
public String toString() {
return "Apple [color=" + color + ", weight=" + weight + "]";
}
}
interface FilterApple{
public boolean test(Apple apple);
}
public class LambdaDemo {
public static void filterApple(List<Apple> apples,FilterApple filter) {
for(Apple apple:apples)
{
if(filter.test(apple))
System.out.println(apple);
}
}
public static void main(String[] args) {
Apple apple1=new Apple("red",50);
Apple apple2=new Apple("red",25);
Apple apple3=new Apple("green",75);
Apple apple4=new Apple("green",100);
List<Apple> apples=new ArrayList<>();
apples.add(apple1);
apples.add(apple2);
apples.add(apple3);
apples.add(apple4);
System.out.println("排序前:");
for(Apple apple:apples)
System.out.println(apple);
Collections.sort(apples,new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.weight-o2.weight>0?1:-1;
}
});
System.out.println("排序后:");
for(Apple apple:apples)
System.out.println(apple);
}
}
再使用Lambda表达式:
package test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Apple{
String color;
int weight;
public Apple(String color, int weight) {
super();
this.color = color;
this.weight = weight;
}
@Override
public String toString() {
return "Apple [color=" + color + ", weight=" + weight + "]";
}
}
public class LambdaDemo {
public static void main(String[] args) {
Apple apple1=new Apple("red",25);
Apple apple2=new Apple("red",20);
Apple apple3=new Apple("green",75);
Apple apple4=new Apple("green",100);
List<Apple> apples=new ArrayList<>();
apples.add(apple1);
apples.add(apple2);
apples.add(apple3);
apples.add(apple4);
System.out.println("排序前:");
for(Apple apple:apples)
System.out.println(apple);
Collections.sort(apples,(Apple o1,Apple o2)->o1.weight-o2.weight);
System.out.println("排序后:");
for(Apple apple:apples)
System.out.println(apple);
}
}
下面这些都是有效的Lambda表达式:
()-> {} :这个Lambda没有参数,并返回void。它类似于主体为空的方法:publicvoid run() {}()-> "Raoul" :这个Lambda没有参数,并返回String作为表达式()-> {return "Mario"; } :这个Lambda没有参数,并返回String(利用显式返回语句)
下面这些都是无效的Lambda表达式:
(Integer i)-> return "Alan"+i; :return是一个控制流语句。要使此Lambda有效,需要使花括号,如下所示:(Integer i)-> {return "Alan"+i; } (String s)-> {"IronMan"; } :“Iron Man” 是一个表达式,不是一个语句。要使此Lambda有效,你可以去除花括号和分号,如下所示:(String s)-> "Iron Man" 。或者如果你喜欢,可以使用显式返回语句,如下所示:(String s)->{return "IronMan"; }
3. 函数式接口
可以在函数式接口上使用Lambda表达式 函数式接口就是只定义一个抽象方法的接口 只有一个抽象方法
Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例
匿名类和Lambda的比较:
4. 使用函数式接口
1. 函数式接口Predicate java.util.function.Predicate接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
package test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class LambdaDemo {
public static void filter(List<String> strs,Predicate<String> predicate) {
for(String str:strs)
{
if(predicate.test(str))
System.out.println(str);
}
}
public static void main(String[] args) {
Predicate<String> notEmpty=(String s)->!s.isEmpty();
List<String> strs=new ArrayList<>();
strs.addAll(Arrays.asList("apple","banana","","orange",""));
System.out.println(strs);
filter(strs, notEmpty);
}
}
2. 函数式接口Consumer java.util.function.Consumer定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
package test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class LambdaDemo {
public static <T> void forEach(List<T> list,Consumer<T> consumer) {
for(T x:list)
consumer.accept(x);
}
public static void main(String[] args) {
forEach(Arrays.asList(1,2,3,4,5), (Integer i)->System.out.println(i));
}
}
3. 函数式接口Function java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
package test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class LambdaDemo {
public static <T,R> List<R> getStrLens(List<T> list,Function<T, R> f) {
List<R> result=new ArrayList<>();
for(T x:list)
result.add(f.apply(x));
return result;
}
public static void main(String[] args) {
List<Integer> strLens=new ArrayList<>();
strLens=getStrLens(Arrays.asList("apple","banana","orange"), (String s)->s.length());
System.out.println(strLens);
}
}
任何函数式接口都不允许抛出受检异常(checked exception)。如果你需要Lambda表达式来抛出异常,有两种办法:定义一个自己的函数式接口,并声明受检异常,或者把Lambda包在一个try/catch块中
5. 类型检查、类型推断以及限制
1. 类型检查
例子:使用Predicate函数式接口,按重量筛选苹果
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
package test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
class Apple{
String color;
double weight;
public Apple(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
@Override
public String toString() {
return "Apple [color=" + color + ", weight=" + weight + "]";
}
}
public class LambdaDemo {
public static <T> void filter(List<T> list,Predicate<T> predicate) {
for(T x:list)
{
if(predicate.test(x))
System.out.println(x);
}
}
public static void main(String[] args) {
Apple apple1=new Apple("red",50);
Apple apple2=new Apple("red",25);
Apple apple3=new Apple("green",75);
Apple apple4=new Apple("green",100);
List<Apple> apples=new ArrayList<>();
apples.add(apple1);
apples.add(apple2);
apples.add(apple3);
apples.add(apple4);
Predicate<Apple> big=(Apple apple)->apple.weight>50;
filter(apples, big);
}
}
2. 同样的Lambda,不同的函数式接口 同一个Lambda可用于多个不同的函数式接口:
3. 类型推断
Predicate<Apple> big=(Apple apple)->apple.weight>50; 可以改写为: Predicate<Apple> big=apple->apple.weight>50;
4. 使用局部变量
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
public static void main(String[] args) {
int portNum=8080;
Runnable r=()->System.out.println(portNum);
new Thread(r).start();
}
注意:局部变量必须显式声明为final,或事实上是final
Local variable portNum defined in an enclosing scope must be final or effectively final
为什么Lambda表达式引用局部变量有final限制?
在上面代码中,主线程中给portNum分配了栈内存,存在这种情况:如果新创建的线程运行了很长时间,此时主线程中的portNum变量已经被回收了,此时新创建的线程又要使用portNum变量。Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。主线程中的原始变量portNum没有了,新线程中的副本portNum还有,但如果portNum只被赋值一次就没有什么影响了,因此就有了这个限制
其次在lambda表达式内部不允许修改外围定义的局部变量的值可以保证多线程情况下的安全
6. 方法引用
方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们
方法引用主要有三类:
- 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)
- 指向任意类型实例方法的方法引用(例如String的length方法,写作String::length)
- 指向现有对象的实例方法的方法引用
代码示例:
public class LambdaDemo {
public static void main(String[] args) {
List<String> strs=Arrays.asList("a","b","A","B");
strs.sort((s1,s2)->s1.compareToIgnoreCase(s2));
System.out.println(strs);
}
}
使用方法引用后如下:
public class LambdaDemo {
public static void main(String[] args) {
List<String> strs=Arrays.asList("a","b","A","B");
strs.sort(String::compareToIgnoreCase);
System.out.println(strs);
}
}
参考: 《Java 8实战》
|