关于Lambda表达式与Stream
说明
在网上看到一个人写的总结,挺好,因此全量摘抄下来,以便理解和查阅
Lambda 表达式
Lambda 语法
Lambda 表达式在Java中引入了一个新的语法元素和操作符 ->
,它将Lambda分成两个部分:
左侧 :指定Lambda表达式需要的所有参数
右侧 :指定了Lambda体,即Lambda表达式要执行的功能
语法格式一: 无参,无返回值,Lambda体只需要一条语句
package com;
public class Test1 {
public static void main(String[] args) {
// 不用lambda
/*Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("I'm run");
}
};*/
Runnable runnable = () -> System.out.println("I'm run");
runnable.run();
}
}
语法格式二: Lambda有一个参数,且无返回值
package com;
import java.util.function.Consumer;
public class Test2 {
public static void main(String[] args) {
//不用lambda
/*Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};*/
Consumer<String> consumer = (s)-> System.out.println(s);
consumer.accept("hi,coulson");
}
}
语法格式三: Lambda有一个参数,参数的小括号可以省略
package com;
import java.util.function.Consumer;
public class Test3 {
public static void main(String[] args) {
Consumer<String> com = x-> System.out.println(x);
com.accept("hi");
}
}
语法格式四:Lambda需要两个参数,并且有返回值,多条语句时必须加上{}
package com;
import java.util.function.BinaryOperator;
public class Test4 {
public static void main(String[] args) {
BinaryOperator<Integer> bi = (x,y) ->{
System.out.println("加法");
return x+y;
};
Integer apply = bi.apply(6,4);
System.out.println(apply);
}
}
语法格式五:当lambda体只有一条语句时,return 与与大括号可以省略
package com;
import java.util.function.BinaryOperator;
public class Test5 {
public static void main(String[] args) {
BinaryOperator<Integer> bi = (x,y) -> x+y;
int result = bi.apply(34,43);
System.out.println(result);
}
}
语法格式六:Lambda的参数列表的类型可以不写,因为jvm编译器可以通过上下文进行"类型推断"
package com;
import java.util.function.BinaryOperator;
public class Test6 {
public static void main(String[] args) {
BinaryOperator<Integer> bi = (x,y) -> x+y;
int result = bi.apply(51,52);
System.out.println(result);
}
}
类型推断
上述Lambda表达式中的参数类型都是由编译器推断得出的。Lambda表达式中无需指定类型,程序依然可以编译,这是因为javac根据程序的上下文,在后台推断出了参数的类型。Lambda表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的"类型推断"
函数式接口
只包含一个抽象方法的接口,成为函数式接口。
可以通过Lambda表达式来创建该接口的对象。(若Lambda表达式跑出一个受检异常,那么该异常需要在目标解耦的抽象方法上进行声明)。
我们可以在任意函数式接口上使用@FunctionalInterface注解,这样可以检查它是否是一个函数式接口,同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。
package com;
@FunctionalInterface
public interface TestFunction {
String getValue(String str);
}
package com;
@FunctionalInterface
public interface TestFunction2<T,R> {
R getValue(T t1, T t2);
}
作为参数传递Lambda表达式
package com;
public class Test6 {
/**
* 数据处理方法
* @param str
* @param function
* @return
*/
public String strHandler(String str, TestFunction function) {
return function.getValue(str);
}
public static void main(String[] args) {
// 自定义函数式接口,并使用Lambda表达式
String result = new Test6().strHandler("abcd", x->x.toUpperCase());
System.out.println(result);
}
}
package com;
public class Test7 {
public void op(Integer num1, Integer num2, TestFunction2<Integer, Integer> tf) {
System.out.println(tf.getValue(num1, num2));
}
public void test3() {
op(20,30,(x,y) -> x+y);
}
public static void main(String[] args) {
new Test7().test3();
}
}
注意:
作为参数传递Lambda表达式:为了将Lambda表达式作为参数传递,接受Lambda表达式的参数类型必须是与该Lambda表达式兼容的函数式接口的类型
Java8内置四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer消费类型接口 | T | void | 对类型为T的对象应用操作,包含方法void accept(T t) |
Supplier 供给型接口 | 无 | T | 返回类型为T的对象,包含方法:T get(); |
Function<T,R>函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法: R apply(T t); |
Predicate断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法boolean test(T t); |
package com;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
public class TestConsumer {
public void consumer(Map<String, Double> money, Consumer<Map<String,Double>> con){
con.accept(money);
}
public static void main(String[] args) {
Map<String,Double> map = new HashMap<>();
map.put("money",100000d);
new TestConsumer().consumer(map,x-> x.put("money",x.get("money") - 2000d));
System.out.println(map);
}
}
package com;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
public class TestSupplier {
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
list.add(sup.get());
}
return list;
}
public static void main(String[] args) {
List<Integer> list = new TestSupplier().getNumList(3, () -> {
Random random = new Random();
int i = random.nextInt(100);
System.out.println("产生整数:" + i);
return i;
});
System.out.println(list);
}
}
package com;
import java.util.UUID;
import java.util.function.Function;
public class TestFunction {
public String converString(String str, Function<String,String> function) {
return function.apply(str);
}
public static void main(String[] args) {
String name = "hello";
String coverString = new TestFunction().converString(name, (x)->x+ UUID.randomUUID().toString());
System.out.println(coverString);
}
}
package com;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class TestPredicate {
public List<String> converList(List<String> list, Predicate<String> predicate) {
List<String> result = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
if (predicate.test(list.get(i))) {
result.add(list.get(i));
}
}
return result;
}
public static void main(String[] args) {
List<String> stringList = Arrays.asList("张飞","刘备","贾宝玉","赵云");
List<String> result = new TestPredicate().converList(stringList,(x) ->x.length() > 2);
System.out.println(result);
}
}
其他接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
BiFunction<T,U,R> | T,U | R | 对类型为T,U参数应用操作,返回R类型的结果。包含方法为R apply(T t,U u); |
UnaryOperator (Function子接口) | T | T | 对类型为T的对象进行意愿运算,并返回T类型的结果。包含方法为T apply(T t); |
BinaryOperator (BiFunction子接口) | T,T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(T t1,T t2); |
BiConsumer<T,U> | T,U | void | 对类型为T,U参数应用操作。包含方法为void accept(T t,U u); |
ToIntFunction ToLongFunction ToDoubleFunction | T | int long double | 分别计算int,long,double,值的函数 |
IntFunction LongFunction DoubleFunction | int long double | R | 参数分别为int,long,double类型的函数 |
方法引用与构造器引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)方法引用:使用操作符"::"将方法名和对象或类的名字分割开来。
如以下三种主要使用情况
对象::实例方法
类::静态方法
类::实例方法
package com;
import org.junit.Test;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 一.方法引用:
* 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
* 方法引用:使用操作符"::"将方法名和对象或类的名字分隔开来。
* <p>
* 三种情况:对象::实例方法
* 类::静态方法
* 类::实例方法
* <p>
* 注意:
* Lambda体中调用方法的参数列表或返回值类型,必须与函数式接口中抽象方法的函数列表或返回值类型保持一致
* 当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时:ClassName::methodName(例如:test4)
* <p>
* 二.构造器引用
* 格式:ClassName::new
* 注意:构造器参数列表要与接口中抽象方法的参数列表一致
* 三.数据引用
* 格式:type[]::new
*/
public class TestMethodRef {
/**
* 对象::实例方法
*/
@Test
public void test1() {
Consumer<String> con = (x) -> System.out.println(x);
con.accept("haha");
Consumer<String> conRef = System.out::println;
conRef.accept("hehe");
}
/**
* 对象::实例方法
*/
@Test
public void test2() {
Employee employee = new Employee("sky", 30);
Supplier<String> supplier = () -> employee.getName();
String str = supplier.get();
System.out.println(str);
Supplier<String> suRef = employee::getName;
System.out.println(supplier.get());
}
/**
* 类::静态方法
*/
@Test
public void test3() {
Comparator<Integer> comparable = (x, y) -> Integer.compare(x, y);
int result = comparable.compare(5, 6);
System.out.println("result=" + result);
Comparator<Integer> comRef = Integer::compare;
comRef.compare(8, 9);
System.out.println("result=" + result);
}
/**
* 类::实例方法
*/
@Test
public void test4() {
//当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时,才能使用
BiPredicate<String, String> bi = (x, y) -> x.equals(y);
boolean res = bi.test("test1", "test2");
System.out.println("res=" + res);
BiPredicate<String, String> biRef = String::equals;
res = biRef.test("test1", "test2");
System.out.println("res=" + res);
Employee employee = new Employee("tom", 20);
Function<Employee, String> function = (e) -> e.getName();
String strRes = function.apply(employee);
System.out.println("strRes=" + strRes);
Function<Employee, String> funcRef = Employee::getName;
strRes = function.apply(employee);
System.out.println("strRes=" + strRes);
}
/**
* 构造器引用
*/
@Test
public void test5() {
Supplier<Employee> supplier = () -> new Employee();
System.out.println(supplier.get());
Supplier<Employee> supRef = Employee::new;
System.out.println(supRef.get());
Function<Integer, Employee> function1 = (x) -> new Employee(x);
Function<Integer, Employee> funcRef1 = Employee::new;
System.out.println(funcRef1.apply(30));
Function<String, Employee> function2 = (x) -> new Employee(x);
Function<String, Employee> funcRef2 = Employee::new;
System.out.println(funcRef2.apply("Ronaldo"));
}
/**
* 数组引用
*/
@Test
public void test6() {
Function<Integer, String[]> function = (x) -> new String[x];
String[] ss = function.apply(5);
System.out.println(ss.length);
Function<Integer, String[]> funRef = String[]::new;
ss = funRef.apply(15);
System.out.println(ss.length);
}
}
接口中的默认方法与静态方法
Java 8 中允许接口中包含具有具体实现的方法,该方法称为"默认方法",默认方法使用default关键修饰。
package com;
public interface ITest1 {
default String getName() {
return "男人";
}
static void show() {
System.out.println("this is the method of interface ");
}
}
接口默认方法"类优先"原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
package com;
public interface ITest2 {
default String getName() {
return "ITest2.getName";
}
}
package com;
public interface ITest3 {
default String getName() {
return "ITest2.getName";
}
static void show() {
System.out.println("接口中的静态方法");
}
}
package com;
public class MyClass {
public String getName() {
return "MyClass.getName";
}
}
package com;
public class SubClass extends MyClass implements ITest2,ITest3 {
@Override
public String getName() {
return ITest3.super.getName();
}
public static void main(String[] args) {
SubClass subClass = new SubClass();
System.out.println(subClass.getName());
}
}
接口中的静态方法
public interface ITest3 {
default String getName() {
return "ITest2.getName";
}
static void show() {
System.out.println("接口中的静态方法");
}
}