- (参数类型 参数名称) -> {执行代码语句}
说明:
()
里面放接口中抽象方法的参数列表;->
新语法格式,代表指向动作;{}
里面放重写抽象方法的执行语句。()
中的参数的数据类型可省略;/*
无参无返回值
*/
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
}).start();
//使用lambda表达式转换匿名内部类//
new Thread(() -> {System.out.println("hello");}).start();
//用省略格式写
new Thread(()->System.out.println("hello")).start();
/*
有参有返回值
*/
Collections.sort(list,new Comparator<Student>(){
@Override
public int compart(Student o1,Student o2) {
return o1.getScore() - o2.getScore();
}
});
//使用lambda标准格式
Collections.sort(list,(Student o1,Student o2) -> {return o1.getScore() - o2.getScore();});
//使用lambda省略格式
Collections.sort(list, (o1,o2) ->o1.getScore() - o2.getScore());
参数列表->重写方法体
。参数列表->重写的抽象方法
,一旦函数式接口实例调用抽象方法,执行的就是lambda的方法体。函数式接口:有且只有一个抽象方法的接口
适用于函数式编程的场景,其体现正是Lambda表达式!
“语法糖”指原理不变而更为便捷的代码语法。
正常格式:
修饰符 interface 接口名 {
public abstract 返回值类型 方法名(参数列表);
//其他非抽象方法内容
}
函数式接口简略格式:
public interface MyFunctionalInterface {
void myMethod();
}
定义:
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod();
}
应用:
public class Note {
private static void Test(MyFunctionalInterface mfi) {
mfi.myMethod();
}
public static void main(String[] args) {
Test(()->System.out.println("Do something!"));
//使用匿名内部类还原
Test(new MyFunctionalInterface() {
@Override
public void myMethod() {
System.out.println("Do something!");
}
});
}
}
示例解析:先定义一个自定义函数式接口。由于Test方法传入的参数是函数式接口的实现类对象,所以在调用静态方法Test()时,示例用Lambda表达式代替匿名内部类作为该对象,并重写抽象方法。
要理解为何在Lambda表达式外是写Test,因为main方法里调用的是Test方法,而不是mfi或者myMethod!后两者都只是调用Test方法所需要的东西,Lambda表达式已经充当后两者
定义:
@FunctionalInterface
public interface Sumable {
int sum(int a, int b);
}
应用:
public class Note {
private static void countSum (int x, int y, Sumable cal) {
System.out.println(cal.sum(x, y));
}
public static void main(String[] args) {
countSum(1,2,(i,j)->i+j);
//使用匿名内部类还原
countSum(1, 2, new Sumable() {
@Override
public int sum(int a, int b) {
return a + b;
}
});
}
}
示例解析:同样定义一个函数式接口,调用静态方法countSum,第三个参数需要传入函数式接口的实现类对象,则用lambda表达式替代匿名内部类传入方法。
延迟执行:在需要的时候才执行
解决部分场景中某些代码执行后不一定被使用所导致的性能浪费的问题
1.8的新特性
通过类名或对象名引用已存在的方法以简化lambda表达式。
方法引用和方法调用时两种概念,方法调用时马上执行。而方法引用在没有执行函数式接口的抽象方法时,该抽象方法的重写方法并不会被执行。
类名::方法名
对象名::方法名
看起来有点像方法调用的格式
语法格式:ClassName::staticMethodName
(类名::静态方法名)
使用场景
比如Lambda表达式:array->ArrayUtils.getMax(array)
,可以简化成ArrayUtils::getMax
,注意观察规律
注意事项
被引用的方法和函数式接口中的抽象方法必须有相同的参数列表;
若函数式接口的抽象方法有返回值,则被引用的方法必须也有相同的返回值;
若函数式接口的抽象方法无返回值,被引用方法的返回值可有可无。
在使用前,需要判断上述条件,均满足之后lambda表达式才能用方法引用简化,以下三种类型同理
示例代码
需求:使用静态方法引用,调用已存在的方法获得数组的最小值
/*
使用静态引用前提:有函数式接口,有被引用的静态方法,lambda仅调用一个被引用的方法,以及上文提到的注意事项
*/
/*函数式接口*/
@FunctionalInterface
interface ArrayInterface {
//返回最小值
int min(int[] arr);
}
/*数组工具类,内含被引用的静态方法*/
class ArrayTools {
public static int getMinValue(int[] arr) {
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
return min;
}
}
public class Notes03 {
public static void main(String[] args) {
int[] arr = {11,53,68,15,86,1,2,16,5};
//1.直接调用静态方法
int min1 = ArrayTools.getMinValue(arr);
System.out.println("min1 = " + min1);
System.out.println("----------------");
//2.使用ArrayInterface实现类对象(匿名内部类)
ArrayInterface ai1 = new ArrayInterface(){
@Override
public int min(int[] arr) {
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
return min;
}
};
int min2 = ai1.min(arr);
System.out.println("min2 = " + min2);
System.out.println("----------------");
//使用lambda替换匿名内部类
ArrayInterface ai2 = array -> {
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
return min;
};
int min3 = ai2.min(arr);
System.out.println("min3 = " + min3);
System.out.println("----------------");
//使用方法引用简化lambda
ArrayInterface ai3 = ArrayTools::getMinValue;
/*
上述代码等价于:
ArrayInterface ai3 = new ArrayInterface(){
//重写抽象方法
int min(int[] arr) {
//重写的抽象方法中仅调用一个被应用的静态方法,相当于方法套方法,而两个方法的参数列表也必须相同
return ArrayTools.getMinValue(int[] arr);
}
}
}
*/
int min4 = ai3.min(arr);
System.out.println("min4 = " + min4);
}
}
语法格式:instance::methodName
(对象名::非静态方法名)
使用场景
比如Lambda表达式:
MyRandom myRandom = new MyRandom; (a,b)->myRandom.nextIntAtoB(a,b)
,可以简化成myRandom::nextIntAtoB
,注意观察规律
注意事项
跟静态方法引用一致
示例代码
需求:使用对象方法引用,调用已存在的方法获得随机数
/*函数式接口*/
@FunctionalInterface
interface getRandomNumber {
// 获得a到b之间的随机数
int randomAToB(int a,int b);
}
/*被引用方法*/
class RanNumber {
public int randomNum(int a,int b) {
Random r = new Random();
return r.nextInt(b - a + 1) + a;
}
}
public class Notes04 {
public static void main(String[] args) {
//1.使用匿名内部类
getRandomNumber rn1 = new getRandomNumber() {
@Override
public int randomAToB(int a, int b) {
Random r = new Random();
return r.nextInt(b - a + 1) + a;
}
};
int ranNum1 = rn1.randomAToB(10, 20);
System.out.println("ranNum1 = " + ranNum1);
System.out.println("-------*---------");
//2.使用lambda替代
getRandomNumber rn2 = (a, b) -> new Random().nextInt(b - a + 1) + a;
int ranNum2 = rn2.randomAToB(10, 20);
System.out.println("ranNum2 = " + ranNum2);
System.out.println("-------*---------");
//3.lambda另一写法,为了方便理解后面的方法引用
//先创建被引用方法的实例
RanNumber n3 = new RanNumber();
getRandomNumber rn3 = (a, b) -> n3.randomNum(a, b);
int ranNum3 = rn3.randomAToB(10, 20);
System.out.println("ranNum3 = " + ranNum3);
System.out.println("-------*---------");
//4.因为lambda只调用一个方法,可用方法引用。因为被引用方法不是静态,所以要用对象方法引用
RanNumber n4 = new RanNumber();
getRandomNumber rn4 = n4::randomNum;
/*
上式等价于:
getRandomNumber rn4 = new getRandomNumber() {
//重写抽象方法
int randomAToB(int a, int b){
//重写的抽象方法只调用一个非静态方法
return n4.randomNum(int a, int b);
}
}
}
*/
int ranNum4 = rn4.randomAToB(10, 20);
System.out.println("ranNum4 = " + ranNum4);
}
}
语法格式:Class::new
(类名::new)
使用场景
比如Lambda表达式:
brand->new Car(brand)
,可以简化成Car::new
,注意观察规律
注意事项
示例代码
需求:运用构造方法引用,创建新的顾客对象
/*函数式接口*/
@FunctionalInterface
interface login{
Guest loginGuest(String name);
}
/*引用类*/
class Guest {
private String name;
public Guest() {
}
public Guest(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Guest{" +
"name='" + name + '\'' +
'}';
}
}
public class Notes06 {
public static void main(String[] args) {
//1.匿名内部类
login l1 = new login() {
@Override
public Guest loginGuest(String name) {
return new Guest(name);
}
};
Guest guest1 = l1.loginGuest("达尔文");
System.out.println(guest1);
//2.lambda
login l2 = name -> new Guest(name);
Guest guest2 = l2.loginGuest("高尔夫");
System.out.println(guest2);
//3.构造方法引用
login l3 = Guest::new;
/*
上式等价于:
login l3 = new login() {
//重写抽象方法
Guest loginGuest(String name) {
//抽象方法调用构造方法,必须保证有对应的构造方法
return new Guest(name);
}
};
*/
Guest guest3 = l3.loginGuest("马克思");
System.out.println(guest3);
}
}
数组作为Object的子类,具有构造方法,但语法稍有不同。若要应用Lambda,需要一个函数式接口:
@FunctionalInterface
public interface ArrayBuilder {
int[] buildArray(int length);
}
使用lambda表达式:
public class Notes05 {
private static int[] initArray(int length, ArrayBuilder builder) {
return builder.buildArray(length);
}
public static void main(String[] args) {
int[] array = initArray(10, length-> new int[length]);
//用匿名内部类还原
int[] array1 = initArray(10, new ArrayBuilder() {
@Override
public int[] buildArray(int length) {
return new int[length];
}
});
}
}
简化lambda
public class Notes05_Another {
private static int[] initArray(int length, ArrayBuilder builder) {
return builder.buildArray(length);
}
public static void main(String[] args) {
int[] array = initArray(10, int[]::new);
}
}
以下两种写法是等效的
length -> new int[length]
int[]::new
示例代码
需求:字符串数组忽略大小写排序
public class Notes07 {
public static void main(String[] args) {
String[] strs = {"niahao","Jonny","JoJo","GioGio","mango","123"};
//使用lambda,忽略大小写
Arrays.sort(strs,(o1, o2) -> o1.compareToIgnoreCase(o2));
for (String str : strs) {
System.out.println(str);
}
System.out.println("---------------");
//使用方法引用
Arrays.sort(strs,String::compareToIgnoreCase);
/*
上式等价于
Arrays.sort(strs, new Comparator() {
//重写抽象方法
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2); //o1调用方法对除o1以外的参数进行比较
}
});
*/
for (String str : strs) {
System.out.println(str);
}
}
}
示例解释
类名::实例方法名
进行方法引用时,一定是Lambda表达式所接受的第一个参数来调用实例方法,如果lambda表达式接受多个参数,则其余的参数将被作为被引用方法的参数传递进去this::成员方法
的格式来进行方法引用。(对象方法引用的特殊情况,对象名就是this)补充示例代码
针对上例,以下两种写法是等效的:
() -> this.bookMsg()
this::bookMsg
super::成员方法
的格式来进行方法引用。(对象方法引用的特殊情况,对象名就是super)示例代码
需求:利用方法引用,子类调用父类方法
@FunctionalInterface
public interface Helper {
void help();
}
public class Father {
public void sayHello(){
System.out.println("你好");
}
public void sayBye(){
System.out.println("再见");
}
}
public class Son extends Father {
@Override
public void sayHello() {
System.out.println("hi");
}
@Override
public void sayBye() {
System.out.println("886");
}
public void helpFather(Helper helper) {
helper.help();
}
//在子类中调用父类的方法
public void greeting() {
helpFather(super::sayHello);
/*
上述代码等价于:
helpFather(()->super.sayHello());
*/
helpFather(super::sayBye);
}
}
针对上例,以下两种写法是等效的:
Lambda表达式: `() -> super.sayHello()`
super::sayHello
java.util.function
包提供大量的函数式接口java.util.function.Supplier
接口仅包含一个无参的方法:T get()
,用来获得一个泛形参数指定类型的对象数据示例代码
需求:使用Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值.
public class homework11 {
public static void main(String[] args) {
int[] arr = {15,1,3,165,168,31,18,3,51,33,155,4,53};
/*
抽象方法是在lambda表达式中重写,就像抽象方法在匿名内部类重写一样!!
这里重写的是Supplier接口的void get()方法
*/
int max = getMax(() -> {
int maxValue = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > maxValue) {
maxValue = arr[i];
}
}
return maxValue;
});
/*
上述代码等价于:
int max = getMax(new Supplier{
//重写抽象方法
void get(){
//下面的是重写方法的内容:
int maxValue = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > maxValue) {
maxValue = arr[i];
}
}
return maxValue;
}
});
*/
System.out.println("max = " + max);
}
public static int getMax(Supplier<Integer> supplier) {
return supplier.get();
}
}
java.util.function.Consumer
接口与Supplier接口相反,会消费一个数据。void accept(T t);
示例代码
需求:使用Consumer接口实现字符串大小写
public class Notes09 {
public static void main(String[] args){
String str = "HelloWorld";
// 消费者1:将字符串转换为大写输出
Consumer<String> one = s -> System.out.println(s.toUpperCase());
// 消费者2:将字符串转换为小写输出
Consumer<String> two = s -> System.out.println(s.toLowerCase());
/*
可以直接按顺序消费
one.accept(str);
two.accept(str);
也可以通过andThen方法指定顺序,而且可以像链式结构一样一直顺延
Consumer c = one.andThen(two);
c.accept(str);*/
//简化
one.andThen(two).accept(str);
}
}
需求:根据字符串数组,按照指定格式输出
public class homework12 {
public static void main(String[] args) {
String[] array = { "deep♂dark♂fantacy,男♂", "搞♂比利,男♂", "如花,女♂" };
//指定消费者
Consumer<String> c1 = new Consumer<String>() {
@Override
public void accept(String s) {
String[] strs = s.split(",");
System.out.print("姓名:" + strs[0] + "。");
}
};
Consumer<String> c2 = s -> System.out.println("性别:" + s.split(",")[1] + "。");
//遍历数组
for (String str : array) {
//按序执行
c1.andThen(c2).accept(str);
}
}
}
java.util.function.Predicate
场景判断,满足条件返回true,否则false
格式:boolean test(T t)
使用格式:Predicate
//示例
public class Notes01 {
private static void method(Predicate<String> one) {
boolean isLong = one.test("Helloworld");//根据外面传入的判断条件调用test(T t),将t放入到lambda中进行判断,返回布尔值
System.out.println("字符串是否包含H:" + isValid);
}
public static void main(String[] args) {
method(s ‐> s.contains("H"));//lambda作为判断条件传入method中
}
}
类似于条件判断“与”
使用格式:Predicate
其中one,two只是接口实现对象名,可以用其他名字代替。
//示例
public class Notes01 {
private static void method(Predicate<String> one, Predicate<String> two) {
boolean isValid = one.and(two).test("Helloworld");
System.out.println("字符串符合要求吗:" + isValid);
}
public static void main(String[] args) {
method(s ‐> s.contains("H"), s ‐> s.contains("W"));
}
}
类似于条件判断“或”
使用格式:Predicate
其中one,two只是接口实现对象名,可以用其他名字代替。
//示例
public class Notes01 {
private static void method(Predicate<String> one, Predicate<String> two) {
boolean isValid = one.or(two).test("Helloworld");
System.out.println("字符串符合要求吗:" + isValid);
}
public static void main(String[] args) {
method(s ‐> s.contains("H"), s ‐> s.contains("W"));
}
}
Predicate predicate.negate().test(T t)
//示例
public class Notes01 {
private static void method(Predicate<String> one) {
boolean isLong = one.negeate().test("Helloworld");
System.out.println("字符串是否包含H:" + isValid);
}
public static void main(String[] args) {
method(s ‐> s.contains("H"));
}
}
java.util.function.Function
数据从一个类型转换为另一种类型。前者称为前置条件,后者称为后置条件。R=>T其中最主要的apply方法为:`R apply(T t)`,根据T的参数的值获得对应的类型为R的结果。比如数据类型的转换。
public class Demo11FunctionApply {
private static void method(Function<String, Integer> function) {
int num = function.apply("10");
System.out.println(num + 20);
}
public static void main(String[] args) {
//lambda
method(s ‐> Integer.parseInt(s));
//方法引用
method(Integer::parseInt);
}
}
Consumer的andThen方法 | Function的andThen方法 |
---|---|
仅是将两个相互独立的操作排序执行 | 上一个操作执行的结果将作为下一个操作执行的参数 |
Functionone.andThen(Function two).apply(t);