概要
Java Lambda表达式是一种匿名函数,他是没有声明的方法,即没有访问修饰符,返回值声明和名字
作用
1. 传递行为,而不仅仅是值
2. 提升抽象层次
3. API重用性更好
4. 更加灵活
Lambda结构
- 一个Lambda表达式可以有零个或多个参数
- 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
- 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a,b)或者(int a,int b)或(String a,String b,float c)
- 空圆括号代表参数集为空。例如:()-> 42
Lambda 箭头左侧(参数)->箭头右侧(执行体) 当左侧没有参数时 左侧括号是不能省略的
public class Test1 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 3, 8);
/*
1. @FunctionalInterface 凡是一个类加上该注解 都是函数式接口
请注意,加了改接口可以使用
lambda表达式、方法引用或构造函数引用。
2.满足以下规则 不然系统会给你生成一个错误信息
1.类型是接口类型,而不是批注类型、枚举或类
2.满足函数式接口要求
*/
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
//我们可以将行为理解为对应函数式接口方法的实现方法
System.out.println(integer);
}
});
}
}
关于函数式接口:
1.如果一个接口只有一个抽象方法,那么改接口就是一个函数式接口
2.如果我们在某个接口声明@FunctionalInterface注解,那么编译就会暗中函数式接口定义来要求该接口
3.如果某个接口只有一个抽象方法,但我们并没有该接口声明@FunctionalInterface注解。那么编译器依旧会将该接口看做函数式接口。
4.注意,函数接口的实例可以用lambda表达式、方法引用或构造函数引用创建
Iterable
jdk1.5 ,实现该接口的类可以进行迭代 ,1.8以后集合都可以直接使用默认方法forEach方法进行迭代
(jdk8之后 接口可以写默认方法和 static实现方法)
forEach
doc文档
对Iterable的每个行为执行给定的操作,直到处理完所有行为或操作引发异常为止。除非实现类另有指定,否则按迭代顺序(如果指定了迭代顺序)执行操作。操作引发的异常将取决于调用方
forEach改方法是将==行为动作(函数 在java中一种特别的对象)==当做参数进行传递 在jdk1.8之前方法参数只能传递具体数据
实例1
public class Test1 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 3, 8);
/*
1. @FunctionalInterface 凡是一个类加上该注解 都是函数式接口
请注意,加了改接口可以使用
lambda表达式、方法引用或构造函数引用。
2.满足以下规则 不然系统会给你生成一个错误信息
1.类型是接口类型,而不是批注类型、枚举或类
2.满足函数式接口要求
*/
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
list.forEach(x-> System.out.println(x));
Consumer<Integer> s = x-> System.out.println(x);
}
}
Consumer(重要)
doc文档
代表着一个接受单个输入参数且不返回结果的操作。与大多数其他函数式接口不同,消费者希望通过副作用(就是可能会去修改接收单个参数)进行操作。类似Test1中accept执行给定行为具体操作给定的行为参数即可
当调用该接口可以直接使用Lambda表达式操作 如accept方法 ( T x)->方法体
我们可以对流中元素的属性进行修改
Student student = new Student();
List<Student> integers = Arrays.asList(student);
List<Student> students = integers.stream().peek(x -> {
x.setScore(90);
x.setUsername("zhangsan");
}).collect(Collectors.toList());
System.out.println(students);
}
项目中如何使用?
实例2
@FunctionalInterface
interface MyIterfece {
void test();
// void test2(String s);
/*
为什么函数式接口里有两个抽象方法 系统不报错
因为 该接口的实现类 一定继承于Object基类 基类存在toString()方法 无需实现类进行创建 所以函数式接口允许存在
Object基类的抽象方法
*/
@Override
String toString();
}
class Test2 {
public void meTest(MyIterfece myIterfece) {
System.out.println("1");
myIterfece.test();
System.out.println("2");
}
public static void main(String[] args) {
Test2 test2 = new Test2();
test2.meTest(new MyIterfece() {
@Override
public void test() {
System.out.println("mytest");
}
});
/*
当函数式接口里的方法没有参数时 ()不能省略
因为函数式接口只有一个抽象方法 所以方法名就不那么重要
*/
test2.meTest(() -> System.out.println("mytest"));
System.out.println("====================================");
//等同于MyIterfece接口实现类
MyIterfece myIterfece = () -> System.out.println("mytest");
System.out.println(myIterfece.getClass());
System.out.println(myIterfece.getClass().getSuperclass());
//打印该实现类实现的接口
System.out.println(myIterfece.getClass().getInterfaces()[0]);
}
}
打印结果
/*
1
mytest
2
1
mytest
2
====================================
MyIterfece接口的实现类
class com.shengsiyuan.jdk8.Test2$$Lambda$2/1603195447
实现类接口的父类
class java.lang.Object
实现类实现的接口
interface com.shengsiyuan.jdk8.MyIterfece
*/
传统外部迭代:通过下标一个一个迭代出数据(for循环 增强for 迭代器)
内部迭代: 从内部以一个取出数据
例3:将集合英文变成大写
public class Test3 {
public static void main(String[] args) {
// /*
// 对于lambda表达式来说 完全不用管函数式接口里方法名是什么,因为其实通过上下文进行类型推断
// 只需要关系返回值和参数即可
// */
// TheInterface theInterface = () -> {
// };
// System.out.println(theInterface.getClass().getInterfaces()[0]);
//
// TheInterface2 theInterface2 = () -> {
// };
// System.out.println(theInterface2.getClass().getInterfaces()[0]);
//
// //lambda表达式必须依附上下文定位到对应类型 如果没有上下文就会报错
()->{};
//
//
// new Thread(() -> System.out.println("thread")).start();
//将集合英文变成大写
List<String> list = Arrays.asList("hello", "world", "hello world");
// List<String> list2 = Lists.newArrayList();
// list.forEach(x -> list2.add(x.toUpperCase()));
// list2.forEach(x -> System.out.println(x));
/*
采用stream流的方式来编写
map 映射的意思就是将一个值映射为另外一个值
Stream方法中也有个forEach方法作用类似Iterable接口的forEach
*/
// list.stream().map(x -> x.toUpperCase()).forEach(y -> System.out.println(y));
/*
采用方法引用的方式
x -> x.toUpperCase() 等价于 String::toUpperCase
*/
list.stream().map(String::toUpperCase).forEach(System.out::println);
/*
Function<String,String>
输入是调用toString lambda对象的第一个参数 x(也就是String对象) ( x -> x.toUpperCase() 等价于 String::toUpperCase)
输出是toString方法返回的值
*/
Function<String, String> function = String::toString;
System.out.println(function.getClass().getInterfaces()[0]);
}
@FunctionalInterface
interface TheInterface {
void myMethod();
}
@FunctionalInterface
interface TheInterface2 {
void myMethod2();
}
}
例4
public class StringComparator {
public static void main(String[] args) {
/*
集合倒叙排序
*/
List<String> list = Arrays.asList("zhangsan", "zhaojie", "maliu");
Collections.sort(list, (x, y) -> x.compareTo(y));
System.out.println(list);
}
}
在例三中stream().map(Function<? super T, ? extends R> mapper)
Function(重要)
Function<String,String>
输入:是调用toString lambda对象的第一个参数 x(也就是String对象) ( x -> x.toUpperCase() 等价于 String::toUpperCase)
返回:是toString方法返回的值
*/
Function<String, String> function = String::toString;
System.out.println(function.getClass().getInterfaces()[0]);
//map参数就是Function<T,R> 此时 将x当做参数,x.toUpperCase() 当做值返回
list.stream().map(x -> x.toUpperCase()).forEach(y -> System.out.println(y));
例5
public class FunctionTest {
public static void main(String[] args) {
FunctionTest functionTest = new FunctionTest();
System.out.println(functionTest.function(5, x -> x * 2));
/*
x -> x +2 是一个行为 当调用方法时他将 x +2 这种行为动作传过去
执行到function.apply(a)时 此时才会执行改行为 执行完返回对应数据
此时行为是用的时候传过去对应的行为(加减乘除)
*/
System.out.println(functionTest.function(10, x -> x + 2));
System.out.println(functionTest.function(10, x -> x * x));
System.out.println(functionTest.function2(10, x -> x + "你好啊!"));
//传统方式
System.out.println(functionTest.method(2));
}
//接受一个参数和一个行为
public int function(int a, Function<Integer, Integer> function) {
Integer apply = function.apply(a);
return apply;
}
public String function2(int a, Function<Integer, String> function) {
String apply = function.apply(a);
return apply;
}
/*
传统方法,我们需要提前将行为定义好 再去调用对应的方法
在运行之前行为都是定义好的做不到灵活多变
*/
public int method(Integer a) {
return a * 5;
}
public int method2(Integer a) {
return a + 2;
}
public int method3(Integer a) {
return a * a;
}
}
10
12
10你好啊!
Function中两个默认方法 一个静态方法
/*
总是返回始终返回调用方
其输入参数的函数
*/
static <T> Function<T, T> identity() {
return t -> t;
}
/*
组合函数 将传过来的Function行为apply执行结果 当做第二个Function的行为的执行参数
返回apply结果
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));//返回结果
}
/**
组合函数 与上面相反 是将当前的Function行为apply执行结果 当做传过来Function的行为的执行参数
返回apply结果
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
//t = 当前Function调用andThen的对象行为
return (T t) -> after.apply(apply(t));
}
例6
Function<String, String> function3 = y -> y + "1";
Function<String, String> function2 = x -> x + "nihao";
//将String对象 传过去
Function<String, String> andThen = function2.andThen(function3);
Function<String, String> compose = function2.compose(function3);
//返回结果根据用户传过来的的行为决定 ,参数再去执行对应的行为
String andThenapply = andThen.apply("22");
System.out.println(andThenapply);
System.out.println("=======================================");
String composeapply = compose.apply("22");
System.out.println(composeapply);
打印结果
22nihao1
=======================================
221nihao//先执行y -> y + "1".apply("22") 得到的结果221 在执行 x -> x + "nihao".apply(221)