Big_DATA_008 lambda表达式

8、lambda表达式和内部类

重点:

8.1、使用lambda表达式实现接口

8.2、lambda表达式的基础语法

8.3、lambda表达式的语法进阶

次重点:

8.4、lambda表达式的函数引用

8.1、lambda表达式简介

8.1.1、lambda表达式的简介

lambda表达式jdk1.8之后推出的新特性。本质上来说,lambda表达式就是一个匿名函数。可以使用lambda表达式简洁的实现接口中的方法。

8.1.2、lambda的小案例
interface Calculate {
    int calculate(int a, int b);
}
class Addition implements Calculate {
    @Override
    public int calculate(int a, int b) {
        return a + b;
    }
}

class Program {
    public static void main(String[] args) {
        // 给接口类型的引用进行赋值,右侧需要是这个接口的实现类类型对象。
        Calculate calculate = new Addition();
        int result = calculate.calculate(10, 20);
        
        // 如果使用lambda表达式来完成同样的操作
        Calculate calculate2 = (a, b) -> a + b;
        int result2 = calculate2.calculate(10, 20);
    }
}
8.1.3、lambda表达式对接口的要求

并不是所有的接口都可以使用lambda表达式来实线。lambda表达式毕竟只是一个匿名方法,如果接口中只有一个方法必须要实现,则可以使用lambda表达式;但是如果接口中必须要实现的方法比较多。

lambda表达式只能对 函数式接口 进行简洁的实现。

8.2、lambda表达式的基础语法

lambda表达式,从本质来讲,就是一个匿名方法。一个方法的组成,有 返回值、方法名、参数、方法体 部分。

而lambda表达式是匿名方法,所以,方法名可以忽略。另外,在lambda表达式中,返回值类型也可以忽略。

因此,对于lambda表达式只需要关注两点即可:

  • 参数 : 以()括起来,将参数写入到()中。
  • 方法体 : 以 {} 括起来,将方法体写入到 {} 中。

对于lambda表达式,还需要记住一个新的语法: ->

  • lambda运算符 -> : 可以分隔参数和方法体。
// 说明需要实现一个无参、无返回值的方法
() -> { System.out.println("hello world"); }
// 说明需要实现一个 (int, int) 参数的、int返回值的方法
(int a, int b) -> { return a + b; }
// 无参无返回
NoneParameterNoneReturn lambda1 = () -> {
	System.out.println("hello world");
};
lambda1.test();			// 最终的实现,是lambda表达式中的实现


// 多个参数、有返回
MutipleParameterSingleReturn lambda2 = (int x, int y) -> {
	return x + y;
};
int ret2 = lambda2.test(10, 20);
System.out.println(ret2);
8.3、lambda表达式的语法进阶

lambda表达式是为了简洁的实现接口的,所以某些部分是可以省略的。

8.3.1、参数的精简

1、参数的类型,已经在接口的方法中有明确规定了,因此在lambda表达式中,可以省略参数的类型。

注意:如果要省略形参的类型,则每一个形参都必须省略。不能出现有的形参带类型,有的没有。

MutipleParameterNoneReturn lambda1 = (a, b) -> {
	System.out.println("a = " + a + ", b = " + b);
};
lambda1.test(10, 20);

2、如果参数列表中,形参的数量只有一个,此时可以省略小括号。

SingleParameterNoneReturn lambda2 = a -> {
	System.out.println("a = " + a);
};
lambda2.test(10);
8.3.2、方法体的精简

1、如果方法体中只有一句语句,则大括号可以省略。

SingleParameterNoneReturn lambda2 = a -> System.out.println("a = " + a);
lambda2.test(10);

2、如果方法中唯一的一条语句是返回语句,则大括号省略的同时,return也必须省略。

MutipleParameterSingleReturn lambda3 = (a, b) -> a + b;
int result = lambda3.test(10, 30);
System.out.println(result);
8.4、lambda表达式的函数引用

推理:

lambda表达式可以简洁的实现一个接口,但是,一般情况下,在lambda表达式中的都是简单的逻辑。如果需要处理的逻辑比较复杂,不推荐将复杂的逻辑直接的写到lambda表达式中。

如果需要在lambda表达式中进行的复杂的逻辑处理,已经在其他的方法中实现过了,可以直接调用这个方法即可。或者可以使用函数引用,将引用的函数,实现接口中的方法。

public class CSyntax {
	public static void main(String[] args) {
		// 需求: 
		// 计算a和b的最大公约数
		
		// 这是个函数引用,使用calculate方法实现接口中的方法
		MutipleParameterSingleReturn lambda = CSyntax::calculate;
		
		int result = lambda.test(12, 8);
		System.out.println(result);
	}
	
	public static int calculate(int a, int b) {
		// 1、找出最小值
		int min = a > b ? b : a;
		// 2、从最小值开始,向下递减直到1,找到一个能够同时整除a和b的数字
		for (int i = min; i >= 1; i--) {
			if (a % i == 0 && b % i == 0) {
				return i;
			}
		}

		return -1;
	}
}

构造方法的引用

如果接口中的方法,需要返回一个对象,则此时,可以引用构造方法。

public class DSyntax {
	public static void main(String[] args) {
		/*
		GetPerson lambda = () -> new Person();
		Person xiaoming = lambda.get();
		System.out.println(xiaoming);
		*/
		// 引用的构造方法
		GetPerson lambda2 = Person::new;
		System.out.println(lambda2.get("xiaoming", 10));
	}
}

interface GetPerson {
	Person get(String name, int age);
}


class Person {
	String name;
	int age;
	
	public Person() {
		super();
		System.out.println("Person的无参构造方法执行了");
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
		System.out.println("Person的有参构造方法执行了");
	}
	
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
}
8.5、系统内置的函数式接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VqYslWlU-1571049674888)(./images/functional.png)]

8.6、闭包

1、在lambda表达式中,使用到的局部变量,必须是常量。

public static void main(String[] args) {
	int a = 10;
	NoneParameterNoneReturn lambda = () -> {
		// 此时,这里会有问题,因为在lambda表达式中是用到了局部变量a,则这个变量默认就是常量。
        a = 20;	
	};
}

2、如果在一个lambda表达式中引用到了某一个局部变量,此时,lambda表达式会提升这个局部变量的生命周期,直到lambda表达式也执行结束。

public class EClosure {
	public static void main(String[] args) {
		
		NoneParameterNoneReturn ret = test();
		
		ret.test();
	}
	
	private static NoneParameterNoneReturn test() {
		// 定义一个局部变量
		int a = 10;
		
		NoneParameterNoneReturn lambda = () -> {
			System.out.println(a);
		};
		
		return lambda;
	}
	
}
8.7、内部类

定义在一个类内部,或者是定义在一个方法内部的类,叫做内部类。

8.7.1、成员内部类(了解)

写在一个类的内部,与属性、方法平级的类。并且这样的类,没有使用static修饰。这种类,可以看做是外部类的一个成员。

  • 成员内部类的访问权限可以是:任意权限。

  • 实例化的时候,需要借助外部类对象来完成

    // 1、实例化外部类对象
    Outter outter = new Outter();
    // 2、实例化内部类对象
    Outter.Inner inner = outter.new Inner();
    
  • 编译后会生成 外部类$内部类.class 文件

8.7.2、静态内部类(了解)

写在一个类的内部,与属性、方法平级的类。并且这样的类,用static修饰。这样的类,叫做静态内部类。

  • 静态内部类的访问权限可以是:任意权限。

  • 实例化的时候,直接实例化即可,但是需要用外部类.内部类的形式来访问,或者导包。

    Outter.StaticInner inner2 = new Outter.StaticInner();
    
  • 编译后会生成 外部类$内部类.class 文件

8.7.3、局部内部类(了解)

写在一个代码段中,与局部变量相似。这个类仅在这个代码段中生效。

  • 局部内部类访问权限:没有,局部内部类是不能加访问权限修饰符的,仅在当前代码段中生效。
  • 实例化的时候,与外部类的形式一模一样。
  • 编译后会生成 外部类$序号内部类.class 文件
8.7.4、匿名内部类
// 实例化一个狗的对象
Dog dog = new Dog() {
	// 这里,就是一个匿名内部类
	// 上方的new,实例化的是一个匿名内部类的对象
	// 这个匿名内部类和Dog的关系:继承关系。这个匿名内部类,继承自Dog类。
};

在匿名内部类中,一般不去添加新的成员,因为即便添加了,向上转型后的对象也不能访问。在匿名内部类中,常常是对父类的某些方法进行重写。

匿名内部类,常常和抽象类、接口配合使用

配合抽象类使用:

Animal animal = new Animal() {
	@Override
	public void walk() {
		System.out.println("匿名子类用蹦的");
	}
};
animal.walk();

配合接口使用:

MutipleParameterSingleReturn obj = new MutipleParameterSingleReturn() {
	@Override
	public int test(int a, int b) {
		return 0;
	}
};
  • 配合抽象类使用,实例化的是抽象类的匿名子类对象
  • 配合接口使用,实例化的是接口的匿名实现类对象
  • 匿名类的对象实例化完成后,会进行向上转型。

Animal animal = new Animal() {
@Override
public void walk() {
System.out.println(“匿名子类用蹦的”);
}
};
animal.walk();


配合接口使用:

```java
MutipleParameterSingleReturn obj = new MutipleParameterSingleReturn() {
	@Override
	public int test(int a, int b) {
		return 0;
	}
};
  • 配合抽象类使用,实例化的是抽象类的匿名子类对象
  • 配合接口使用,实例化的是接口的匿名实现类对象
  • 匿名类的对象实例化完成后,会进行向上转型。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Lambda表达式是C++11引入的一种函数对象,可以在需要函数对象的地方使用,比如作为函数参数、返回值等。Lambda表达式的语法形式如下: ``` [capture list](parameters) mutable exception -> return type { // function body } ``` 其中,`capture list` 表示捕获列表,用于捕获外部变量。`parameters` 表示函数参数列表,`mutable` 用于表示是否可以修改值传递的变量,`exception` 是异常列表,`return type` 表示返回类型,`function body` 则是函数体。 在Lambda表达式中,可以通过 `[this]` 捕获当前对象的指针,即 `this` 指针,可以方便地访问当前对象的成员变量和成员函数。例如: ``` class MyClass { public: void foo() { int x = 1; auto lambda = [this, x]() mutable { this->m_member_var += x; this->m_member_function(); x++; }; lambda(); } private: int m_member_var; void m_member_function(); }; ``` 在上面的例子中,Lambda表达式通过 `[this, x]` 捕获了当前对象的指针和 `foo()` 函数中定义的变量 `x`。在 Lambda 表达式中可以通过 `this->m_member_var` 和 `this->m_member_function()` 访问当前对象的成员变量和成员函数。由于 `x` 是值传递的,所以在 Lambda 表达式中需要使用 `mutable` 关键字使其可修改,可以通过 `x++` 修改变量的值。最后调用 `lambda()` 执行 Lambda 表达式。 需要注意的是,Lambda表达式捕获 `this` 指针时,需要保证当前对象是有效的,即不能在已经销毁的对象中访问成员变量和成员函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值