一,lambda表达式【函数表达式】
- 函数表达式:就是一个数学的方程式 函数表达式就是一个值的式子 是一个数值。
- 概述:lambda 是函数式接口的子类对象。他是匿名内部类的简化变形形式。
- 使用:具有自己的固有格式。
- 标准格式:(参数列表)-> { 表达式做事的逻辑代码 }
- 格式元素说明:
- 三个一 : 一些参数 一个箭头 一段代码
- (参数列表): lambda 表达式需要的资源 【花括号中做事需要的数据】就是匿名内部类重写抽象方法的方法参数列表【原封不动拿过来】
- -> : 他是lambda的标识,
- {代码}: 表达式做事的逻辑代码【就是匿名内部类中重写的那个方法的方法体】
- 格式可以省略:
- 小括号省略原则:
- 参数类型直接省略【有上下文推断】
- 如果参数有且只有一个参数,类型和小括号都可以省略
- 无参的时候,小括号不能省略
- 大括号省略原则:
- 有且只有一句语句:可以同时省略 return 、大括号 、分号
- 注意事项:三者要省略同时全部省略,不能单一省略。
- 整体的注意事项:小括号和大括号的省略原则互不干涉
代码示例
public class Demo_Lambda02 {
public static void main(String[] args) {
// lambda表达式的省略原则
// 小括号省略
InterfaceA lambda_a = a -> {System.out.println(a);}; // 接口A里面是一个参数
InterfaceA2 lambda_a2 = () -> {System.out.println("花花");}; // 接口A2里面是空参
InterfaceA3 lambda_a3 = (a, b) -> {System.out.println(a + b);}; // 接口A3里面有两个参数
// 用对象调用方法打印方法体中的内容
lambda_a.show(22); // 22
lambda_a2.show(); // 我爱你
lambda_a3.show(1, 1); // 2
// 大括号省略原则
InterfaceA lambda_a11 = a -> System.out.println(a); // 接口A里面是一个参数
InterfaceA4 lambda_a44 = (a, b) -> a + b; // 一条执行语句
InterfaceA4 lambda_a55 = (a, b) -> {
int c = a + b;
return c;
};//方法体中有两条执行语句,不满足省略原则,有多条语句的时候大括号不能省略
}
}
- 匿名内部类的使用前提:拥有继承或者实现关系
- 本质:父类或父接口的子类或实现类对象
- Lambda的使用前提:
- lambda是匿名内部类的简化变形,所有的匿名内部类都可以使用lamabda吗?
- 不能的,因为lamabda表达式的使用前提是:
- 有且只有一个抽象方法的接口【函数式接口】。
- 使用必须要有上下文推断
- 注意:
- 使用匿名内部类创建接口的对象的时候,会单独编译成为一个字节码文件,意味着对应的是一个对象【在堆空间开辟对象空间了】占用空间,执行效率低
- lambda表达式变形之后没有单独编译成文件,意味着他没有对应的类【可以当做不是一个对象】在内存中使用了一个数值来表示接口的实现类对象这样不用单独的开辟空间,节约了内存空间,相对执行效率就提升了
代码示例:
// 定义一个接口InterfaceA
public interface InterfaceA {
//有且只有一个抽象方法
void show(int a);
}
//定义一个测试类
public class Demo_Lambda {
public static void main(String[] args) {
InterfaceA aa = (int a)->{System.out.println(a);};
System.out.println(aa); //com.ujiuye.demo.Demo_Lambda$$Lambda$23/0x0000000800b47440@7ba4f24f
// 创建interfaceA接口的实现类对象,采用匿名内部类创建
InterfaceA aaa = new InterfaceA() {
@Override
public void show(int a) {
System.out.println(a);
}
};
System.out.println(aaa);//打印匿名内部类对象的地址值,com.ujiuye.demo.Demo_Lambda$1@7699a589
//lambda表达式的体现 [是匿名内部类的变形]
InterfaceA aaaa = (int a)->{System.out.println(a);};
System.out.println("-----------------------------");
//调用实现类中的方法show 对象来调用
//匿名内部类对象调用
aaa.show(100);
//使用lambda 表达式的值来调用
aaaa.show(200);
}
}
- 总结:
- lambda 表达式是匿名内部类的变形,表示一个函数接口的对象
- lambda 表达式的小括号就是抽象方法的参数列表,大括号就是抽象方法重写后的方法体
- lambda 表达式的大括号需要出效果,必须要使用对象【lambda】进行调用对应的方法
- 需要我们掌握的是:
- lambda 的标准格式和省略原则
- lambda 的使用前提:函数式接口、有上下文推断
二,方法引用【lambda的孪生兄弟】
- 概述:lambda表达式的执行体【大括号】里面的代码如果已经被另一个类中的某个方法完成过,就直接把这个类的方法引用过来使用,就不重新书写了。
- 说白点:这又是一个进一步偷懒的行为。lambda表达式的大括号内容如果别的类的方法书写过了,lambda自己就不写了把别人的拿过来用。用别人的就得有牺牲,牺牲了自己的格式变成另外一种格式,把这种新的格式命名为方法引用。
- 格式:【::】符号就是方法引用的标识
- 普通方法: 对象::方法名 比如: System.out :: print
- 静态方法: 类名::方法名 比如: Math :: abs
- 构造方法: 类名::new
- 就是lambda的执行体和某个类中某个方法的执行体一模一样,就可以舍弃lambda表达式,选用方法引用。
- 作用:
- 可以把一个类中已有的方法调用作为函数式的具体体现来使用,提升了代码的复用性
- 可以把一个类中已有的方法调用作为函数式的具体体现来使用,直接将方法作为值来传递。
- 本质:代表接口的实现类对象【实际是值】方法引用是lambda表达式的孪生兄弟。
- 他们的使用前提是一样的。方法引用的范围比 lambda 表达式小。
代码示例
//定义一个接口 InterfaceA
public interface InterfaceA {
//有且只有一个抽象方法
void show(int a);
}
//定义一个接口 InterfaceA5
public interface InterfaceA5 {
//有且只有一个抽象方法
String show();
}
//定义一个接口 InterfaceA6
public interface InterfaceA6 {
//有且只有一个抽象方法
int show(int a);
}
//定义测试类
public class Demo_Lambda03 {
public static void main(String[] args) {
InterfaceA lambda_a11 = a -> System.out.println(a);
//输出语句是不是System.out的println方法的方法体
InterfaceA lambda_a111 = System.out::println; //引用了out流对象的普通方法println
lambda_a111.show(100);//100为实际参数
System.out.println("----------------");
InterfaceA5 lambda5= ()->{return new String();};
//变形
InterfaceA5 lambda55= String ::new; //引用了string类的空参构造
String s = lambda5.show();
String ss = lambda55.show();
System.out.println(s); //空参构造内部没有内容
System.out.println(ss); //空参构造内部没有内容
System.out.println(s+"a"); //a
System.out.println(ss+"b"); //b
//创建接口6的实现类对象
InterfaceA6 a6= (int a)->{return Math.abs(a);}; //方法的重写,abs是静态方法
//lambda表达式的方法体 是不是使用math 类的abs方法
//变形
InterfaceA6 a61= Math::abs;
int i=a61.show(-12);
System.out.println(i);
}
}
三,函数式接口
- 概述:有且只有一个抽象方法的接口就叫函数式接口。比如: Runnable
- 注解:@FunctionalInterface
- 作用:接口上写上这个注解,编译的时候jvm就会自动的判断所在接口是不是函数式接口。是编译通过,不是编译不通过报错了。主要使用于函数式接口的定义
- 【判断接口是不是函数式接口】
代码示例:
@FunctionalInterface
public interface InterfaceA {
void show();
//void print();
}
四,常用内置函数式接口
- 概述:jdk1.8之后的API中提供的函数式接口,我们直接使用lambda表达式进行使用
-
消费型接口【Consumer
< T >
】 - 抽象方法:void accept (T t) ; 给我一个数据我能消费掉【使用掉】
- 作用:
当某个函数【函数式接口的抽象方法】可以接收一个数据,并且处理这个数据,处理完成之后,不需要返回任何数据,这个函数需要当做数据来进行传递,就使用消费型接口
以前只能传递要处理的数据,现在也可以传递处理数据的方式
- print 方法中想要使用 accept 这个方法的方法逻辑 ,以前方法不能传入到另外一个方法当中,想办法把 accept 方法放到一个接口中,使用接口作为 print 方法的参数来传递。
代码示例
import java.util.function.Consumer;
public class Demo_Consumer {
public static void main(String[] args) {
// 使用这个接口 匿名内部类
// 步骤:先写实现类---重写抽象方法---调用方法
new Consumer<Integer>() {
@Override
public void accept(Integer t) {
// 消费掉一个数据
System.out.println("我使用了" + t + "元钱");
};
}.accept(100);
// 使用lambda表达式实现接口
Consumer consumer = (t) -> {System.out.println("我使用了" + t + "元钱");}; // 消费掉一个数据
// 这属于下文,consumer不是一个对象是一个数值 [内部封装的是一个方法的逻辑]
consumer.accept(100);
System.out.println("------------------------");
// 调用print 方法
print((t) -> {System.out.println("我使用了" + t+ "元钱");}, 100);
}
//使用lambda参数是把重写的方法逻辑 [消费一个数据逻辑] 变成一个值进行传递
//相当于把方法逻辑转变到print方法中来了
public static void print(Consumer c, Integer i) {
c.accept(i); // 打开方法逻辑
System.out.println("买了一个瓜");
}
//体现的不是代码使用方式的改变,改变的是编程的思想
//以前传递的是匿名内部类对象,体现的是面向对象思想
//现在传递的是lambda表达式,他是一个值,体现的是面向结果
}
- 总结:
// 体现的不是代码使用方式改变,改变的是编程的思想
// 以前传递的是匿名内部类对象,体现的是面向对象思想
// 现在传递的lambda表达式,他是一个值,体现的是面向结果
https://www.cnblogs.com/greatLong/p/12191457.html 参考资料
-
供给型接口【Supplier
< T >
】 - 名称:供给型接口 【无中生有】
- 抽象方法:T get()
- 作用:
可以生产任意一个需要的数据,当做数据来进行传递
代码示例
import java.util.function.Supplier;
public class Demo_Supplier {
public static void main(String[] args) {
//使用匿名内部类来使用
//数据类型为String
String s = new Supplier< String >() {
@Override
public String get() {
return "如花似玉的翠花"; //方法体
}
}.get();//调用get方法
System.out.println(s); //如花似玉的翠花
//使用lambda表达式
//用这个变量(supplier)接收值
//重写抽象方法 get()为空参
//Supplier supplier = ()->{return "如花似玉的翠花";};
Supplier supplier = ()-> "如花似玉的花花"; //变形后
System.out.println(supplier.get());//如花似玉的花花
//方法引用不能使用
}
}
-
函数型接口【Function
<T, R>
】【转换型接口】 - 抽象方法:R apply (T t)
- 作用:把一个资源转换成为另外一个资源。
- 默认功能: andThen(Function f):连接两次数据转换的功能 先按照第一个接口的转换方式转换然后再按照第二个接口的方式继续转换
代码示例
import java.util.function.Function;
public class Demo_Function {
public static void main(String[] args) {
//直接用lambda表达式来使用
//两个泛型一个是String类型,另一个是Integer类型
//Integer.parseInt(s)将字符串s转变为数字
Function<String,Integer > f1 = (s)->{return Integer.parseInt(s);};
// Integer num= f1.apply("2");
// System.out.println(num); //2 ,将一个字符串2转变为Integer类型的2
Function<Integer,Integer > f2 = (s)->{return s*s*s*8;};
// Integer i = f2.apply(num);
// System.out.println(i); //64
//将上面的两个合二为一
Integer num1 = f1.andThen(f2).apply("2");
System.out.println(num1); //64
Integer num2 = f1.andThen(f2).andThen(f2).apply("2");
System.out.println(num2); //2097152---64*64*64*8
}
}
-
断言型接口【Predicate
<T>
】【判断型接口】 - 抽象方法:boolean test(T t)
- 作用:
接收一个数据,判断数据是否合法【判断标准自己重写】,返回一个boolean结果
- 默认方法:
Predicate and(Predicate pre):两个接口判断后的结果按照且的关系进一步判断整
Predicate or(Predicate pre):两个接口判断后的结果按照或的关系进一步判断整合
Predicate negate():返回的是调用者判断条件的取反
代码示例
import java.util.function.Predicate;
public class Demo_Predicate {
public static void main(String[] args) {
//lambda表达式
Predicate<String >predicate = (s)->{return s.endsWith("a");}; //判断字符串是否以字母a结尾的?
boolean b = predicate.test("abacda");
System.out.println(b); //true
Predicate<String >predicate1 = (s)->{return s.endsWith("m");}; //判断字符串是否以字母m结尾的?
System.out.println(predicate1.test("abacda")); //false
//默认方法
boolean c = predicate.and(predicate1).test("abacda"); //true 且 false = false
System.out.println(c); //false
boolean d = predicate.or(predicate1).test("abacda"); //true 或 false = true
System.out.println(d); //true
System.out.println(predicate.negate().test("abacda")); //false 非
//上面lambda的表达式不能使用方法引用,endswith方法参数没有上下文推断
/*
* Predicate<String >predicate1 = new String ("a")::endsWith;
boolean c = predicate1.test("abacda");
System.out.println(c); //false
*/
}
}