参考资料
[1]. 疯狂Java讲义(第三版) 李刚
Lambda表达式
Lambda表达式入门
允许使用更简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例,先来一个示例:
接口Commond
/**
* Commond interface
*
* @author shuaige
* @date 2018/01/23
*/
public interface Commond {
/**
* 接口里定义的process方法用于封装“处理行为”
* @param target
*/
void process(int[] target);
}
使用传入内部类的写法
/**
* CommandTest class
*
* @author shuaige
* @date 2018/01/25
*/
public class CommandTest {
public static void main(String[] args)
{
ProcessArray pa = new ProcessArray();
int[] target = {3, -4, 6, 4};
pa.process(target, new Commond() {
@Override
public void process(int[] target) {
int num = 0;
for (int tmp : target)
{
num += tmp;
}
System.out.println("数组元素的总和是:" + num);
}
});
}
}
使用Lambda表达式改写
/**
* CommandTest class
*
* @author shuaige
* @date 2018/01/25
*/
public class CommandTest2 {
public static void main(String[] args)
{
ProcessArray pa = new ProcessArray();
int[] array = {3, -4, 6, 4};
pa.process(array, (int[] target) -> {
int num = 0;
for (int tmp : target)
{
num += tmp;
}
System.out.println("数组元素的总和是:" + num);
});
}
}
Lambda表达式的初步应用:
Eatable接口
/**
* Eatable interface
*
* @author shuaige
* @date 2018/01/25
*/
public interface Eatable {
void taste();
}
Flyable接口
/**
* Flyable interface
*
* @author shuaige
* @date 2018/01/25
*/
public interface Flyable {
void fly(String weather);
}
Addable接口
/**
* Addable interface
*
* @author shuaige
* @date 2018/01/25
*/
public interface Addable {
int add(int a, int b);
}
使用示例:
public class LambdaQs {
/**
* 调用该方法需要Eatable对象
* @param e
*/
public void eat(Eatable e)
{
System.out.println(e);
e.taste();
}
/**
* 调用该方法需要Flyable对象
* @param f
*/
public void drive(Flyable f)
{
System.out.println("我正在驾驶:" + f);
f.fly("【碧空如洗的晴日】");
}
/**
* 调用该方法需要Addable对象
* @param add
*/
public void test(Addable add)
{
System.out.println("5与3的和为:" + add.add(5, 3));
}
public static void main(String[] args)
{
LambdaQs lq = new LambdaQs();
/**
* Lambda的代码块只有一条语句,所以可以省略花括号
*/
lq.eat(()-> System.out.println("苹果的味道不错"));
lq.drive(weather -> {
System.out.println("今天的天气是:" + weather);
System.out.println("直升机飞行平稳");
});
/**
* Lambda表达式只有一条语句,可以省略花括号
* 代码块中只有一条语句,即使该表达式需要返回值,也可以省略return关键字
*/
lq.test((a, b) -> a+b);
}
}
Lambda表达式与函数式接口
将Lambda表达式赋值给函数式接口类型的变量
// Runnable是Java本身提供的一个函数式接口
// Runnable接口中只包含一个无参数的方法
// 因此下面的Lambda创建了一个Runnable对象
Runnable r = () -> {
System.out.println("输出...");
}
如果把表达式赋值给一个非函数式接口或参数不对应的接口会报错
Object o = () -> {
System.out.println("输出...");
}
使用函数式接口对Lambda表达式进行强制类型转换
Object o = (Runnable)() -> {
System.out.println("输出...");
}
Lambda表达式实现的匿名方法与目标类型目标(函数式接口)中唯一的抽象方法有相同的形参列表即可。
首先定义一个接口
@FunctionalInterface
public interface TestInterface{
void run();
}
再把上面的代码强制转换为这接口
Object o = (TestInterface)() -> {
System.out.println("输出...");
}
方法引用与构造器引用
简单来说,就是可以在Lambda表达式中直接使用某个类的方法或构造器,不用手动在这里临时写了,很方便。
Converter 接口
/**
* 函数式接口
*/
@FunctionalInterface
public interface Converter {
/**
* 转换字符串为数字
* @param from
* @return
*/
Integer convert(String from);
}
引用类方法
使用Lambda表达式来创建一个Converter 对象
// 自己编写
Converter converter1 = from -> Integer.valueOf(from);
// 引用类Integer类方法的valueOf方法
Converter converter2 = Integer::valueOf;
// 测试
Integer i1 = converter1.convert("99");
Integer i2 = converter2.convert("99");
引用特定对象的实例方法
// 自己编写
Converter converter1 = from -> "www.baidu.com".indexOf(from);
// 引用String类的indexOf实例方法
Converter converter2 = "www.baidu.com"::indexOf;
Integer value1 = converter1.convert("b");
Integer value2 = converter2.convert("b");
引用某类对象的实例方法
函数式接口MyTest
/**
* 函数式接口,根据String、int、int三个参数生成一个String返回值
*/
@FunctionalInterface
public interface MyTest {
/**
* 测试方法
* @param a
* @param b
* @param c
* @return
*/
String test(String a, int b, int c);
}
使用示例
// 编写Lambda方法从第某位开始截取到第某位
MyTest mt1 = (a,b,c)-> a.substring(b,c);
String str1 = mt1.test("www.baidu.com", 2, 9);
// 方法引用替代了Lambda表达式:引用某类对象的实例方法
// 函数式接口中被实现方法的第一个参数作为调用者
// 后面的参数全部传给该方法作为参数
MyTest mt2 = String::substring;
String str2 = mt2.test("www.baidu.com", 2, 9);
引用构造器
引用构造器
函数式接口YourTest
/**
* 函数式接口
*/
@FunctionalInterface
public interface YourTest {
JFrame win(String title);
}
使用示例:
// 使用Lambda表达式创建YourTest对象
YourTest yt1 = (String a) -> new JFrame(a);
JFrame jf1 = yt1.win("我的窗口");
// 使用方法的构造器
YourTest yt2 = JFrame::new;
JFrame jf2 = yt1.win("我的窗口");
Lambda表达式与匿名内部类的联系与区别
相同点:
1. 都可以直接访问“effectively final”的局部变量,以及外部类的成员变量(包括示例变量和类变量)。
2. 都可以直接调用从接口中继承的默认方法
区别点:
1. 匿名内部类可以为任意接口创建示例,不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可;但Lambda表达式只能为函数式接口创建实例。
2. 匿名内部类可以为抽象类甚至普通类创建实例,但Lambda只能为函数式接口创建实例。
3. 匿名内部类实现的抽象方法的方法体允许调用接口中的默认方法,但Lambda表达式的代码块不允许调用接口中定义的默认方法。
package com.qunar;
/**
* Lambda表达式与匿名内部类的联系和区别
*/
public class LambdaAndInner {
private int age = 12;
private static String name = "百度";
public void test()
{
String book = "一本书";
Displayable dis = ()->{
// 访问“effectively final”的局部变量
System.out.println("book局部变量为:" + book);
// 访问外部类的实例变量和类变量
System.out.println("外部类的age实例变量为:" + age);
System.out.println("外部类的name类变量为:" + name);
// 下面会报错,无法接口里的其他默认方法
// System.out.println(add(3, 5));
};
dis.display();
// 调用dis对象从接口中继承的add()方法
System.out.println(dis.add(3, 5));
}
public static void main(String[] args)
{
LambdaAndInner lambds = new LambdaAndInner();
lambds.test();
}
}