介绍
Lambda,在我现在的技术来看,就是为了简化代码的
它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理。
lambda表达式格式
(参数类型 参数名称) ‐> { 代码语句 }
使用lambda的前提条件
有且仅有一个抽象方法的接口,称为“函数式接口”。
使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。使用Lambda必须具有上下文推断。也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
省略规则
在Lambda标准格式的基础上,使用省略写法的规则为:
小括号内参数的类型可以省略;如果小括号内有且仅有一个参,则小括号可以省略;如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
注意
Lambda表达式可以引用类成员和局部变量,但是会将这些变量隐式得转换成final的
使用lambda表达式优化
优化多线程
package com;
public class Demo01ThreadNameless {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println('多线程任务执行!');
}
}).start();
}
}
lambda到底优化什么?
不再有“不得不创建接口对象”的束缚,不再有“抽象方法覆盖重写”的负担,就是这么简单!
package com;
public class Demo02ThreadNameless {
public static void main(String[] args) {
new Thread(() -> System.out.println('多线程任务执行!')).start();
}
}
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
@SpringBootTest(classes = DemoApplication.class)
class DemoApplicationTests {
@Test
void contextLoads() {
Arrays.asList( 'a', 'b', 'd' ).forEach( ( String e ) -> System.out.println( e ) );
Arrays.asList('a','b','c').forEach(e -> System.out.println(e));
// Lambda表达式可以引用类成员和局部变量(会将这些变量隐式得转换成final的)
String separator = ',';
Arrays.asList( 'a', 'b', 'd' ).forEach(
( String e ) -> System.out.println(e + separator) );
}
}
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.Comparator;
@SpringBootTest(classes = DemoApplication.class)
class DemoApplicationTests {
@Test
void contextLoads() {
// 本来年龄乱序的对象数组
Person[] array = {new Person('古力娜扎', 19), new Person('迪丽热巴', 18), new Person('马尔扎哈', 20)};
// 传统写法
Arrays.sort(array, new Comparator() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
// lambda表达式写法
// 如果Lambda表达式中的语句块只有一行,则可以不用使用return语句
Arrays.sort(array,(o1,o2) -> o2.getAge() - o1.getAge());
Arrays.sort(array,(o1,o2) -> {
int i = o2.getAge() - o1.getAge();
return i;
} );
System.out.println(Arrays.toString(array));
}
}
函数式接口
函数式接口在Java中是指:有且仅有一个抽象方法的接口。
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
与@Override注解的作用类似,Java8中专门为函数式接口引入了一个新的注解:@FunctionalInterface。该注解可用于一个接口的定义上:一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
package com.lxit.testnginx.testnginx;
@FunctionalInterface
public interface MyFunctionInterface {
void myMethod();
}
常用函数式接口
JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在javutifunction包中被提供。
Supplier接口
javutifunctioSupplier
仅包含一个无参的方法:Tget()。
用来获取一个泛型参数指定类型的对象数据。
package com.lxit.testnginx.testnginx;
import java.util.function.Supplier;
public class TestSupplier {
private static String getString(Supplier function){
return function.get();
}
public static void main(String[] args) {
String s = 'hello';
String s2 = 'world';
System.out.println(getString(() -> s + s2));
}
}
求数组元素最大值
使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。
package com.lxit.testnginx.testnginx;
import java.util.function.Supplier;
public class TestSupplier {
private static void printMax(Supplier supplier) {
Integer maxNum = supplier.get();
System.out.println(maxNum);
}
public static void main(String[] args) {
int[] array = {10, 20, 100, 30, 40, 50};
printMax(() -> {
int maxNum = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > maxNum) {
maxNum = array[i];
}
}
return maxNum;
}
);
}
}
Consumer接口
javutifunctioConsumer
抽象方法:voidaccept(Tt),意为消费一个指定泛型的数据。基本使用如:
package com.lxit.testnginx.testnginx;
import java.util.function.Consumer;
public class TestConsumer {
private static void consumerString(Consumer consumer,String s) {
consumer.accept(s);
}
public static void main(String[] args) {
consumerString(s -> System.out.println(s),'看不懂');
}
}
默认方法:andThen()
如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合。
而这个方法就是Consumer接口中的default方法andThen。
下面是JDK的源代码:
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) ‐>{
accept(t);
after.accept(t);
}
}
要想实现组合,需要两个或多个Lambda表达式即可,而andThen的语义正是“一步接一步”操作。例如两个步骤组合的情况:
package com.lxit.testnginx.testnginx;
import java.util.function.Consumer;
public class TestConsumerAndThen {
private static void consumerString(Consumer first,Consumer second, String s) {
first.andThen(second).accept(s);
}
public static void main(String[] args) {
consumerString(s -> System.out.println(s.toLowerCase()), s -> System.out.println(s.toUpperCase()),'Big');
}
}
function接口
javutifunctioFunction
Function接口中的抽象方法为:Rapply(Tt),根据类型T的参数获取类型R的结果。使用的场景例如:将String类型转换为Integer类型。
package com.lxit.testnginx.testnginx;
import java.util.function.Function;
public class TestFunction {
private static void stringConvertInteger(Function function, String s) {
Integer apply = function.apply(s);
System.out.println(apply instanceof Integer);
}
public static void main(String[] args) {
stringConvertInteger(s -> Integer.valueOf(s), '9');
}
}
默认方法:andThenFunction接口中有一个默认的andThen方法,用来进行组合操作。
package com.lxit.testnginx.testnginx;
import java.util.function.Function;
public class TestFunction {
private static void stringConvertInteger(Function function, Function function2,String s) {
Integer num = function.andThen(function2).apply(s);
System.out.println(num);
}
public static void main(String[] args) {
stringConvertInteger(s -> Integer.valueOf(s), s -> s += 10, '9');
}
}
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用javutifunctioPredicate
Predicate接口中包含一个抽象方法:booleantest(Tt)。用于条件判断的场景
package com.lxit.testnginx.testnginx;
import java.util.function.Predicate;
public class TestPredicate {
// 抽象方法 test
private static void testType(Predicate predicate,String str) {
boolean boo = predicate.test(str);
System.out.println(boo);
}
// 默认方法 and
private static void testAnd(Predicate firstPredicate, Predicate secondPredicate, String str){
boolean boo = firstPredicate.and(secondPredicate).test(str);
System.out.println(boo);
}
// 默认方法 or
private static void testOr(Predicate firstPredicate, Predicate secondPredicate, String str){
boolean boo = firstPredicate.or(secondPredicate).test(str);
System.out.println(boo);
}
// 默认方法 negate 取反
private static void testNegate(Predicate firstPredicate, String str){
boolean boo = firstPredicate.negate().test(str);
System.out.println(boo);
}
public static void main(String[] args) {
// true
testType(s -> s.length() > 5,'length');
// true
testAnd(s -> s.length() > 5,s -> s.contains('l'),'length');
// true
testOr(s -> s.length() > 10,s -> s.contains('a'),'length');
// true
testNegate(s -> s.length() > 10,'length');
}
}
方法引用
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿什么参数做什么操作。那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑?
冗余的Lambda场景
package com.lxit.testnginx.testnginx;
import java.util.function.Consumer;
public class DemoPrintSimple {
private static void printString(Consumer data, String str) {
data.accept(str);
}
public static void main(String[] args) {
printString(s -> System.out.println(s), 'Hello World');
}
}
方法引用简化
package com.lxit.testnginx.testnginx;
import java.util.function.Consumer;
public class DemoPrintSimple {
private static void printString(Consumer data, String str) {
data.accept(str);
}
public static void main(String[] args) {
printString(System.out::println, 'Hello World');
}
}
方法引用符
只能在使用lambda表达式的时候,才能使用方法引用符。
符号说明:双冒号为方法引用运算符,而它所在的表达式被称为方法引用。
应用场景:如果Lambda要表达的函数方案,已经存在于某个方法的实现中,那么则可以使用方法引用。
如上例中,Systeout对象中有个println(Strin方法,恰好就是我们所需要的,那么对于Consumer接口作为参数,对比下面两种写法,完全等效:Lambda表达式写法:s->Systeout.println(s);拿到参数之后经Lambda之手,继而传递Systeout.println方法去处理。方法引用写法:Systeout::println直接让Systeout中的println方法来取代Lambda。
推导与省略:如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式——它们都将被自动推导。而如果使用方法引用,也是同样可以根据上下文进行推导。函数式接口是Lambda的基础,而方法引用是Lambda的简化形式。
常见引用方式
对象名引用成员方法
package com.lxit.testnginx.testnginx;
import java.util.function.Supplier;
public class DemoMethodRef {
public static void main(String[] args) {
String str = 'hello';
printUP(str::toUpperCase);
}
public static void printUP(Supplier sup) {
String apply = sup.get();
System.out.println(apply);
}
}
类名引用静态方法
package com.lxit.testnginx.testnginx;
import java.util.function.Supplier;
public class DemoMethodRef {
public static void main(String[] args) {
printRanNum(Math::random);
}
public static void printRanNum(Supplier sup) {
Double apply = sup.get();
System.out.println(apply);
}
}
类构造引用
package com.lxit.testnginx.testnginx;
public class Demo09Lambda {
public static void main(String[] args) {
String name = 'tom';
Person person = createPerson(Person::new, name);
System.out.println(person);
}
public static Person createPerson(Function fun, String name) {
Person p = fun.apply(name);
return p;
}
}
数组构造引用
理解历史人物,就是理解现在的人物。
当年明月
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点