Lambda 表达式入门、与函数式的接口、方法引用与构造器引用以及Lambda表达式与匿名内部类的联系与区别、使用Lambda表达式调用Array树的类方法

Lambda表达式入门

Lambda表达式的主要作用就是代替匿名内部类的繁琐语法。它由三部分组成。

  • 形参列表:形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参表的圆括号也可以省略。
  • 箭头(->)。必须通过英文中划线和大于号组成。
  • 代码块。如果代码块只包含一条语句,Lambda表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束。Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而它的代码块中仅有一条省略了return语句,Lambda表达式会自动返回这条语句。

下面程序示范了Lambda表达式的几种简化写法。

interface Eatable{
	void taste();
}
interface Flyable{
	void fly(String weather);
}
interface Addable{
	int add(int a, int b);
}

public class LambdaQs {
	public void eat(Eatable e) {
		System.out.println(e);
		e.taste();
	}
	public void drive(Flyable f) {
		System.out.println("我正在驾驶:" + f);
		f.fly("【碧空如洗的晴天】");
	}
	public void test(Addable add) {
		System.out.println("5与3的和:" + add.add(3, 5));
	}

	public static void main(String[] args) {
		LambdaQs lq = new LambdaQs();
		lq.eat(()->System.out.println("苹果的味道不错"));
		lq.drive(weather ->{
			System.out.println("今天的天气是:" + weather);
			System.out.println("直升机飞行很平稳");
		});
		lq.test((a, b)-> a+b);

	}

}

 Lambda表达式与函数式的接口

Lambda表达式的类型,也被称为“目标类型”,Lambda表达式的目标类型必须是“函数式接口”。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法。类方法,但只能声明一个抽象方法。

如果采用匿名内部类语法来创建函数式接口的实例,则需要实现一个抽象方法,在这种情况下即可采用Lambda表达式来创建对象,该表达式创建出来的对象的目标类型就是这个函数式接口。

由于Lambda表达式的结果就是被当成对象,因此程序中完全可以使用Lambda表达式进行赋值,例如下面代码

//Runnable 接口中只包含一个无参数的方法
//Lambda 表达式代表的匿名方法实现了Runnable接口中唯一的、无参数的方法
//下面的Lambda 表达式创建了一个Runnable对象
Runnable r = ()->{
    for (int i = 0; i < 100 ; i++){
         System.out.println();
}
}

Lambda 表达式有如下两个限制

  1. Lambda 表达式的目标类型必须是明确的函数式接口
  2. Lambda 表达式只能为函数式接口创建对象。Lambda表达式只能实现一个方法,因此它只能为只有一个抽象方法的接口创建对象。 

为了保证Lambda表达式赋值的目标类型是一个明确的函数式接口,可以有如下三种方式。

  1. 将Lambda表达式赋值给函数式接口类型的变量
  2. 将Lambda表达式作为函数式接口类型的参数传给某个方法。
  3. 使用函数式接口对Lambda 表达式进行强制类型转换。 
//这段代码是错的 Object不是函数接口变量
Object obj = ()->{
    for(int i = 0; i < 10 ; i++){
         System.out.println();
}
};

//将对Lambda执行强制类型转换。
Object obj = (Runnable)()->{
    for(int i = 0; i < 10 ; i++){
         System.out.println();
}
};

 同样的Lambda表达式的目标类型完全可能是变化的——唯一的要求是,Lambda表达式实现的匿名方法与目标类型中惟一的抽象方法有相同的形参列表。

定义如下接口

@FuntionalInterface
interface FkTest{
    void run();
}

 上面的函数式接口中仅定义了一个不带参数的方法,因此前面强制转型为Runnable的Lambda表达式也可以强制转为类型FkTest类型——因为FkTest接口中唯一的抽象方法是不带参数的,而该Lambda表达式也是不带参数的。下面代码也是正确的。

Object obj = (FkTest)()->{
    for(int i = 0; i < 10 ; i++){
         System.out.println();
}
};

Java 8 在java.uit.function包下预定义了大量函数式接口,典型地包含如下4类接口。

  • XxxFunction:这类接口中通常包含一个apply()抽象方法,该方法对参数进行处理、转换(apply()方法的处理逻辑由Lambda表达式来实现),然后返回一个新的值。该函数式接口通常对于指定数据进行转换处理。
  • XxxConsumer:这类接口中通常包含一个accept()抽象方法,该方法与XxxFunction接口中的apply()方法基本相似,也负责对参数进行处理,只是该方法不会返回处理结果。
  • XxxxPredicate:这类接口普通常包含一个test()抽象方法,该方法通常用来对参数进行某种判断(test()方法的判断逻辑由Lambda表达式来实现),然后返回一个Boolean值。该接口通常用于判断是否满足特定条件,经常用于进行筛选数据。
  • XxxSupplier:这类接口中通常包含一个getAsXxx()抽象方法,该方法不需要输入参数,该方法会按某种逻辑算法(getAsXxx()方法的逻辑算法由Lambda表达式来实现)返回一个数据。

 方法引用与构造器引用

方法引用和构造器引用可以让Lambda表达式的代码块更加简洁。方法引用和构造器引用都需要使用两个英文冒号。

Lambda表达式支持如下表所示的几种引用方式。

Lambda表达式支持的方法引用和构造器引用
种类 示例 说明 对应的Lambda表达式

引用类方法

类名::类方法 函数式接口中实现方法的全部参数传给该方法作为参数 (a,b,...)->类名.类方法(a,b,...)
引用特定对象实例方法 特定对象::实例方法 函数式接口中被实现方法的全部参数传给该方法作为参数 (a,b,...)->特定对象.实例方法(a,b,...)
引用某类对象实例方法 类名::实例方法 函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数 (a,b,...)-> a.实例方法(b,...)
引用构造器 类名::new 函数式接口中被实现方法的全部参数传给该构造器作为参数 (a,b,...)->new 类名(a,b,...)

1.引用类方法

引用类方法。定义了如下函数式接口。

@functionalInterface
interface Converter
{
     Integer convert (String from);
}

 该函数式接口中包含一个convert()抽象方法,该方法负责将String参数转换为Integer。下面代码使用Lambda表达式来创建一个Converter对象

Converter converter1 = from -> Integer.valueOf(from);

接下来程序就可以调用converter1对象的convert()方法将字符串转换为整数了,代码如下

Integer val = converter1.convert("99");
System.out.println(val);

上面代码可以用如下方法引用进行替换

//方法引用代替Lambda表示式:引用类方法
// 函数式接口中被实现方法的全部参数传给该类方法做诶参数
Converter converter1 = Integer::valueOf;

2. 引用特定对象的实例方法

引用特定对象的方法。先使用Lambda表达式来创建一个Converter对象

//  使用Lambda表达式创建Converter对象
Converter converter2 = from -> "fkit.org".indexOf(from);

程序可以调用converter1对象的convert()方法将字符串转换为整数了,例如下面代码

Integer value = converter2.convert("it");
System.out.println(value);

上面Lambda表达式的代码块只有一行调用“fkit.org”对象的indexOf()实例方的代码,因此可以使用如下方法引用进行替换。

Converter converter2 = "fkit.org"::indexOf;

3.引用某类对象的实例

引用某类对象的实例方法

@functionalInterface

interface MyTest{
    String test(String a, int b, int c);
}

下面代码使用Lambda表达式来创建一个MyTest对象

MyTest mt = (a, b, c)-> a.substring(b,c);

接下来程序就可以调用mt对象的test()方法了,例如下面代码

String str = mt.test("Java I Love you", 2, 9);
System,out.println(str);

上面Lambda表达式的代码块只有一行a.substring(b,c);因此可以使用如下方法引用进行替换

MyTest mt = String::substring;

4.引用构造器

构造器的引用

@FunctionalInterface
interface YourTest{
    JFrame win(String title);
}

下面代码使用Lambda表达式来创建一个YourTest对象

YourTest yt = (String a) -> new JFrame(a);

 接下来程序就可以调用yt对象的win()方法了,如下代码

JFrame jf = yt.win("我的窗口");
System.out.println(jf);

上面Lambda表达式的代码只有一行new JFrame(a)因此可以使用如下构造器引用进行替换

YourTest yt = JFrame :: new;

Lambda表达式与匿名内部类的联系和区别

相同点

下面程序示范了Lambda表达式与匿名内部类的相似之处

 

  • Lambda表达式与匿名内部类一样,都可以直接访问“effectively final”的局部变量,以及外部类的成员变量
  • Lambda表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法。
@FunctionalInterface
interface Displayable{
	void display();
	default int add(int a, int b) {
		return a + b;
		
	}
}

public class LambdaAndInner {
	private int age = 12;
	private static String name = "教育中心";
	public void test() {
		String book = "Java";
		Displayable dis = ()->{
			System.out.println("book 局部变量为:" + book);
			System.out.println("外部类的age实例变量:" + age);
			System.out.println("外部类的name类变量:" + name);
		};
		dis.display();
		System.out.println(dis.add(3, 5));
	}

	public static void main(String[] args) {
		LambdaAndInner lambda = new LambdaAndInner ();
		lambda.test();

	}

}

Lambda表达式与匿名内部类的区别

  • 匿名内部类可以为任意接口创建实例——不管接口包含多少个抽象方法,只要匿名内部类实现所有抽象方法即可:但Lambda表达式只能为函数式接口创建实例。
  • 匿名内部类可以为抽象类甚至普通类创建实例;但Lambda表达式只能为函数式接口创建实例。
  • 匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lambda表达式的代码块不允许调用接口中定义的默认方法。

对于Lambda表达式的代码块不允许调用接口中定义的默认方法的限制,可以尝试对上面的LambdaAndInner.java程序修改,在Lambda的代码块中增加一行:

//尝试调用接口中的默认方法,编译器会报错
System.out.println(add(3,5));

 使用Lambda表达式调用Arrays的类方法

Arrays类的有些方法需要Comparator、XXXOperator、XxxFunction等接口的实例,这些接口都是函数式接口,因此可以使用Lambda表达式来调用Arrays的方法。程序如下

import java.util.Arrays;

public class LambdaArrays {
  public static void main(String[] args) {
		String [] arr1 = new String [] {"java" , "fkava" , "fkit" , "ios" , "android"};
		Arrays.parallelSort(arr1, (o1,o2)-> o1.length() - o2.length());
		System.out.println(Arrays.toString(arr1));
		int [] arr2 = new int [] {3, -4, 25, 16, 30, 18};
		Arrays.parallelPrefix(arr2, (left, right) -> left * right);
		System.out.println(Arrays.toString(arr2));
		long[] arr3 = new long[5];
		Arrays.parallelSetAll(arr3, operand -> operand *5);
		System.out.println(Arrays.toString(arr3));

	}

}

 

 

你可能感兴趣的:(java)