复习:面向函数:函数做变量 / 回调 / 委托 / Lamda / 箭头函数……
但JavaScript可以将箭头函数赋值给任意一个变量,Java呢?
interface IMove{ double move(double speed, int seconds); }
这种接口被称之为函数(式)/功能(性)接口。
@试一试@:接口中添加一个方法声明?
演示:F3到Comparator,以下方法不算作接口方法:
default void stop() {}
static void stop() {}
String toString();
PS:@FunctionalInterface注释
lambda表达式表示函数式接口的实例:
IMove m = (s, t) -> s * t;Java中lambda使用的是单横线箭头(->)
System.out.println(m.move(2.5, 60)); //实例调用其接口方法
演示:其他规则和JavaScript箭头函数一样
一般情况下,lambda类型可以由编译器推断。@想一想@:为什么可以推断?
什么是特殊情况呢?
Lambda表达式最重要的作用就是作为函数参数传递。假设有两个重载的方法,
static void goSchool(IMove move) { static void goSchool(IWalk walk) {
使用了类似的函数式接口:
interface IWalk { //和IMove类似 double move(float speed, long seconds); }演示:编译时错误
goSchool((s,t)->s*t);解决方案:
//要声明参数类型,所有参数的类型就都要声明 goSchool((double s,int t)->s*t);
IMove m = (s, t) -> s * t; goSchool(m);
goSchool((IMove)(s, t) -> s * t);
Java允许在lambda参数前加final,以禁止lambda表达式内部修改该参数。
但如果这样,需要显式的声明参数类型:
IMove m = (final double s, int t) -> { s *= 2; //error return s * t; };
interface IMove<T> { T move(T speed, int seconds); }
PS:以上对应C#中delegate
Lambda表达式可以视为一种匿名函数。
那如果我们需要传递一个已有的命名函数呢?比如把这个方法(注意是方法本身,不是方法运行结果)传递给IMove,IMove move = ??
static double walk(double speed, int seconds) { return speed * seconds; }这时候就需要操作符为双冒号(::)的方法引用了:
IMove move = Main::walk; //静态方法由类名调用 System.out.println(move.move(23.5, 2));除了静态方法以外,还有:
class Person { public double walk(double speed, int seconds) {
IMove m = new Person()::walk; m.move();
class Person { public Person() {
interface IMove { Person getPerson();
IMove m = Person::new; Person fg = m.getPerson();
interface IMove { int[] get(int length); //注意这个length参数
IMove m = int[]::new; int[] students = m.get(10); //得到一个长度为10的数组
interface IMove{ void move(Person person); //第一个参数是Person
class Person{ public void walk() { //这是一个实例方法
IMove m = Person::walk; //但仍然可以用类名调用 //等价于:IMove m = p->p.walk(); m.move(new Person());当(一定要注意这个当)接口方法有参数时,可以用它的第一个(且只能是第一个)参数类型,后加双冒号(::)指定它的某一个匹配实例方法。(我个人不喜欢这种写法,宁愿用箭头,避免和静态方法混淆)
@想一想@:每用一次lamda表达式,就去声明一个函数接口,麻烦不麻烦?
当泛型被引入之后,解决了这个(匿名)函数的方法参数和返回值的类型问题,lambda表达式是不是可以被归类?
好像我们不需要太多的内置函数接口?
比如,以下接口是不是可以合并统一:
interface IMove<T> { void get(T length); } interface IWalk<T> { void get(T length); }
所以Java为我们提供了java.util.function包,里面封装了一些内置的函数接口:
中文名 |
示例 |
参数 |
返回 |
函数 |
Function<T,R> |
T |
R |
谓词 |
Predicate<T> |
T |
boolen |
Consumer |
Consumer<T> |
T |
void |
Supplier |
Supplier<T> |
无 |
T |
复习:泛型参数的实例化
Consumer<Integer> ci = i -> { System.out.println(++i); }; ci.accept();
但多了一个default的andThen()方法:
default Consumer<T> andThen(Consumer<? super T> after) {着重理解:
这样就能够形成连缀:
ci = ci.andThen(i -> { System.out.println(--i); }); ci.accept(100);
甚至更进一步:
ci.andThen(i -> { System.out.println(--i); }).accept(100);断点演示:两个lambda执行的顺序
注意:
Predicate<Integer> greaterThan0 = i -> i > 0;
System.out.println(greaterThan0.test(100));另外三个default方法,套路和Consumer的andThen()一样。为了演示,添加一个:
Predicate<Integer> lessThan100 = i -> i < 100;
System.out.println(greaterThan0.and(lessThan100).test(-2));
System.out.println(greaterThan0.or(lessThan100).test(80));
System.out.println(greaterThan0.negate().test(80));
还有一个static的isEqual()方法:(演示讲解其实现,复习)
Predicate.isEqual(32).test(new Integer(22))
@想一想@:搞这么一个方法干嘛?直接用 == 不香么?
Predicate.<Integer>isEqual(32).or(greaterThan0).test(new Integer(22))
演示说明:and(Predicate<? super T> other)
Predicate<Student> greaterThan0 = i -> i.Age > 0; Predicate<Person> lessThan100 = i -> i.Age < 100; //假如这样可以允许的话 //Predicate<OnlineStudent> lessThan100 = i -> i.Fee < 100; Student fg = new Student(); /* * 1. 在test的时候,greaterThan0和lessThan100都使用的fg * 2. 我们只能控制and()中的lambda泛型参数, * 3. 确保其至少是Student类,这样predict中使用的都是Student有的类成员 */ greaterThan0.and(lessThan100).test(fg);
“标配的”apply()方法
两个default方法,在当前function
Function<Person, Integer> getAge = p -> p.age; System.out.println(getAge //指定i 类型方案1: .compose((Integer i) -> new Person(i)) //指定i 类型方案2: .<Integer>compose(i -> new Person(i)) //还可以使用构造函数方法引用 .<Integer>compose(Person::new) .andThen(a-> a*0.9) .apply(40));
演示:执行顺序 apply() -> compose() -> getAge -> andThen()
注意练习阅读其源代码。
还有更多的function(函数接口名),可以适用于更多的情形:
大家开发中根据实际情况具体选择。
PS:以上内置function对应C#中的Func和Action
其他见:J&C:Lambda表达式
多快好省!前端后端,线上线下,名师精讲
更多了解 加: