Java语言基础(SE)-第三节 面向对象(三) 接口、多态、匿名类、Lambda、方法引用
接口(Interface)
- API(Application Programming Interface)
- 应用编程接口,提供给开发者调用的一组功能(无须提供源码)
- Java 中的接口
- 一系列方法声明的集合,用来定义规范、标准
- 接口就相当于OC中的协议,自定一系列的标准,谁遵守,谁就要实现,只是OC中的协议是可以设置遵守者是否选择性的实现,这里接口一旦遵守是必须实现
接口中可以定义的内容
- 接口的定义:右击->new->interface,可以看到接口跟类特别相似,接口用interface修饰,类用class修饰
- 可以定义:抽象方法、常量、嵌套类型,从 Java 8 开始可以定义:默认方法、静态方法(类方法)
- 上述可以定义的内容都是隐式 public 的,因此可以省略 public 关键字
- 从 Java 9 开始可以定义 private 方法
- 常量可以省略 static、final
- 抽象方法可以省略 abstract,但默认是抽象方法
- 不能自定义构造方法、不能定义(静态)初始化块、不能实例化
- 举例
- 一个child要找家教,不限制职业,只需要家教具备2个能力:教编程、教足球即可
- 因此建立一个接口用来提供家教的标准
- 各种行业的对象要服从这个标准
//接口JiaJiaoAble package com.zh; public interface JiaJiaoAble { //抽象方法、常量 //public abstract void test1(); //可以省略public abstract //void test2(); public final static int a = 10; //可以省略final static int b = 10; //嵌套类型 class A{} //家教接口标准 public abstract void jaoBianCheng(Child child); public abstract void jaoZuQiu(Child child); } //孩子 package com.zh; public class Child { private String name; //家教 private JiaJiaoAble jiaJiao; public Child(String name) { this.name = name; } public String getName() { return name; } //此时的类型为接口的类型,传入的对象必须实现了该接口的方法 public void setJiaJiao(JiaJiaoAble jiaJiao) { this.jiaJiao = jiaJiao; } //学习 public void study() { jiaJiao.jaoBianCheng(this); jiaJiao.jaoZuQiu(this); } } //学生成为一个家教 package com.zh; //implements 实现接口 public class Student implements JiaJiaoAble { public void jaoBianCheng(Child child) { System.out.println("Student 教" + child.getName() + "编程"); } public void jaoZuQiu(Child child) { System.out.println("Student 教" + child.getName() + "足球"); } } //老师成为一个家教 package com.zh; //implements 实现接口 public class Teacher implements JiaJiaoAble { public void jaoBianCheng(Child child) { System.out.println("Teacher 教" + child.getName() + "编程"); } public void jaoZuQiu(Child child) { System.out.println("Teacher 教" + child.getName() + "足球"); } } //main函数 public static void main(String[] args) { Child chi = new Child("jack"); chi.setJiaJiao(new Student()); chi.study(); chi.setJiaJiao(new Teacher()); chi.study(); /* 打印 Student 教jack编程 Student 教jack足球 Teacher 教jack编程 Teacher 教jack足球 */ }
接口的细节
-
接口名称可以在任何使用类型的地方使用
int a = 10; JiaJiaoAble jia;
- 一个类可以通过 implements 关键字实现一个或多个接口,多个接口用逗号隔开
- 实现接口的类必须实现接口中定义的所有抽象方法,除非它是个抽象类
- 如果一个类实现的多个接口中有相同的抽象方法,只需要实现此方法一次
- extends 和 implements 可以一起使用,implements 必须写在 extends 的后面
- 当父类和接口中的方法签名一样时,那么返回值类型也必须一样
- 就是两个方法必须一模一样
- 一个接口可以通过 extends 关键字继承一个或者多个接口
- 当多个父接口中的方法签名一样时,那么返回值类型也必须一样
- 就是两个方法必须一模一样
public class Dog extends Animal implements Eatable { @Override public void eat(String name) { } } public class Student implements JiaJiaoAble , BaoMu{ ... }
- 当多个父接口中的方法签名一样时,那么返回值类型也必须一样
接口的升级问题
- 如果接口需要升级,比如增加新的抽象方法
- 由于接口中的方法类必须实现,因此会导致大幅的代码改动,以前实现接口的类都得改动
- 若想在不改动以前实现类的前提下进行接口升级,从 Java 8 开始,有 2 种方案
- 默认方法(Default Method)
- 静态方法(Static Method)
默认方法(Default Method)
- 用 default 修饰默认方法
- 默认方法只能是实例方法
- 默认方法可以直接在接口中实现,继承者可以不用实现,也可以覆盖实现
//接口 package com.zh; public interface Eatable { default void eat(String name) { System.out.println("Eatable - eat - " + name); } } //Dog package com.zh; public class Dog implements Eatable { } //Cat package com.zh; public class Cat implements Eatable { @Override public void eat(String name) { //先调用接口的实现,接口名称调用 Eatable.super.eat(name); System.out.println("Cat - eat - " + name); } } //main函数 public class Main { public static void main(String[] args) { Dog dog = new Dog(); dog.eat("bone"); Cat cat = new Cat(); cat.eat("fish"); } //打印 Eatable - eat - bone Eatable - eat - fish Cat - eat - fish
- 默认方法的使用
- 当一个类实现的接口中有默认方法时,这个类可以
- 啥也不干,沿用接口的默认实现:
dog.eat("bone");
- 重新定义默认方法,覆盖默认方法的实现:
cat.eat("fish");
- 重新声明默认方法,将默认方法声明为抽象方法(此类必须是抽象类)
- 就是Dog首先用abstract修饰成为抽象类
- 然后声明一个抽象方法
public abstract void eat(String name);
- 啥也不干,沿用接口的默认实现:
- 当一个接口继承的父接口中有默认方法时,这个接口可以
- 啥也不干,沿用接口的默认实现
- 重新定义默认方法,覆盖默认方法的实现
- 重新声明默认方法,将默认方法声明为抽象方法
- 当一个类实现的接口中有默认方法时,这个类可以
- 默认方法的细节
- 如果父类定义的非抽象方法与接口的默认方法相同时,最终将调用父类的方法
- 如果父类定义的抽象方法与接口的默认方法相同时,要求子类实现此抽象方法
- 可以通过 super 关键字调用接口的默认方法
- 如果(父)接口定义的默认方法与其他(父)接口定义的方法相同时,要求子类型实现此默认方法
静态方法(Static Method)
- 接口中定义的静态方法只能通过接口名调用,不能被继承
- 不能被继承意思是,A接口有静态方法a,B接口继承A接口,
B.a()
,调用,是错误的,不能继承
//接口Eatable package com.zh; public interface Eatable { static void eat(String name) { System.out.println("Eatable - eat - " + name); } } //接口Sleepable package com.zh; public interface Sleepable { static void eat(String name) { System.out.println("Sleepable - eat - " + name); } } //接口Dog package com.zh; public interface Dog extends Sleepable, Eatable { static void eat(String name) { System.out.println("Dog - eat - " + name); } } //类Cat package com.zh; public class Cat implements Eatable { } //main函数 Dog.eat("1"); Eatable.eat("2"); Sleepable.eat("3"); //尽管Cat实现Eatable接口,但是却无法通过Cat的任何方式去调用Eatable的接口方法 //错误 Cat.eat(); Cat cat = new Cat(); //错误 cat.eat(); /* Dog - eat - 1 Eatable - eat - 2 Sleepable - eat - 3 */
- 不能被继承意思是,A接口有静态方法a,B接口继承A接口,
抽象类与接口对比
- 抽象类和接口的用途还是有点类似,该如何选择?何时选择抽象类?
- 在紧密相关的类之间共享代码
- 需要除 public 之外的访问权限
- 接口所有的成员都是public
- 需要定义实例变量、非 final 的静态变量
- 接口只能定义常量
- 何时选择接口?
- 不相关的类实现相同的方法,只是定义行为,不关心具体是谁实现了行为
- 想实现类型的多重继承
- 类的继承只能单继承,不能多继承
- 接口可以多继承,类只能单继承
多态(Polymorphism)
- 什么是多态?
- 具有多种形态
- 同一操作作用于不同的对象,产生不同的执行结果
- 多态的体现
- 父类(接口)类型指向子类对象
- 调用子类重写的方法
- JVM 会根据引用变量指向的具体对象来调用对应的方法
- 这个行为叫做:虚方法调用(virtual method invocation)
- 类似于 C++ 中的虚函数调用
-
多态实例
package com.zh; public class Animal { public void speak() { System.out.println("Animal - speak"); } } package com.zh; public class Dog extends Animal { public void speak() { System.out.println("Dog - speak"); } } package com.zh; public class Cat extends Animal { public void speak() { System.out.println("Cat - speak"); }; } /*********以上是类的继承体系,以下是接口的实现体系*******/ package com.zh; public interface Runnable { void run(); } package com.zh; public class Pig implements Runnable { @Override public void run() { System.out.println("Pig - run"); } } package com.zh; public class Person implements Runnable { @Override public void run() { System.out.println("Person - run"); } } //main函数 public static void main(String[] args) { speak(new Dog()); //Dog - speak speak(new Cat()); //Cat - speak run(new Pig()); //Pig - run run(new Person()); //Person - run } static void speak(Animal animal) { animal.speak(); } static void run(Runnable runnable) { runnable.run(); }
类方法的调用细节
-
静态(类)方法不能实现多态
public class Animal { public static void run() { System.out.println("Animal - run"); } } public class Dog extends Animal { public static void run() { System.out.println("Dog - run"); } } Dog.run(); //Dog - run Animal.run(); //Animal - run Dog dog1 = new Dog(); dog1.run();//Dog - run //静态方法不能实现多态 Animal dog2 = new Dog(); dog2.run();//Animal - run
-
多态只适用于实例
成员变量的访问细节
instanceof
-
可以通过 instanceof 判断某个类型是否属于某种类型
public class Animal {} //接口 public interface Runnable {} public class Dog extends Animal implements Runnable {} Object dog = new Dog(); System.out.println(dog instanceof Dog); //true System.out.println(dog instanceof Animal); //true System.out.println(dog instanceof Runnable);//true System.out.println(dog instanceof String);//false
-
instanceof 使用
public class Cat extends Animal { public void miao() { System.out.println("Cat-miao"); } } public class Dog extends Animal{ public void wang() { System.out.println("Dog-wang"); } } speak(new Dog()); //Dog-wang speak(new Cat()); //Cat-miao public static void speak(Animal animal) { if (animal instanceof Dog) { ((Dog) animal).wang(); }else { ((Cat) animal).miao(); } }
匿名类(Anonymous Class)
-
当接口、抽象类的实现类,在整个项目中只用过一次,可以考虑使用匿名类
public interface Eatable { String name(); int energy(); } public class Person { public void eat(Eatable e) { System.out.println("eat - " + e.name() + "-" + e.energy()); } } Person person = new Person(); //接口当做类来实现 person.eat(new Eatable() { @Override public String name() { return "Apple"; } @Override public int energy() { return 100; } });//eat - Apple-100 Eatable beef = new Eatable() { @Override public String name() { return "Apple"; } @Override public int energy() { return 100; } }; Person person2 = new Person(); //多次使用 person2.eat(beef); //eat - Apple-100 person2.eat(beef); //eat - Apple-100 //抽象类 public abstract class Animal { public abstract void run(); } Animal animalx = new Animal() { @Override public void run() { System.out.println("Animal-run"); } }; animalx.run(); //Animal-run
-
特点:
- 匿名类就是一个接口、抽象类的实现,而且不需要类来承载。
- 通常的方式是需要一个类来实现接口方法、继承抽象类,然后创建这个类的实例,然后用实例来调用
- 现在省略了这个承载类,所以叫匿名类
-
即仅仅想使用接口、抽象类中的方法,直接通过匿名类实现即可
Eatable beef2 = new Eatable() { @Override public String name() { return "Apple"; } @Override public int energy() { return 100; } }; beef2.name(); beef2.energy();
- 匿名类就是一个接口、抽象类的实现,而且不需要类来承载。
匿名类的使用注意
- 匿名类不能定义除编译时常量以外的任何 static 成员(同局部类)
- 匿名类只能访问 final 或者 有效 final 的局部变量(同局部类)
- 匿名类可以直接访问外部类中的所有成员(即使被声明为 private)(同局部类)
- 匿名类只有在实例相关的代码块中使用,才能直接访问外部类中的实例成员(实例变量、实例方法)
-
匿名类不能自定义构造方法,但可以有初始化块
Animal animalx = new Animal() { int age; //不能定义除编译时常量以外的任何 static 成员 //static int x; //编译时常量 final static int y =0; //可以有初始化快 { int xxx; } @Override public void run() { System.out.println("Animal-run"); } };
匿名类的常见用途
-
代码传递
public class Times { //接口 public interface Block { void execute(); } public static void test(Block block) { long begin = System.currentTimeMillis(); //执行代码块 block.execute(); long end = System.currentTimeMillis(); double duration = (end - begin) / 1000.0; System.out.println("耗时:" + duration + "s"); } } package com.zh; //注意:需要导入 import com.zh.Times.Block; public class Main { public static void main(String[] args) { //写一个工具类,统计某段代码执行时间 Times.test(new Block() { @Override public void execute() { //需要测试的代码快 //for(int i = 0;i<10; i++) { //System.out.println(i); //} test(); } }); } static void test() { for(int i = 0;i<10; i++) { System.out.println(i); } } }
-
过滤器
//过滤器 public class Files { //接口 public interface Filter { boolean accept(String filename); } public static String[] getAllFileNames(String dir,Filter filter) { //1.先获取dir路径下所有文件 String[] allFileNames = {}; //2. 过滤 for (String filename : allFileNames) { if(filter.accept(filename)) { //将符合条件的文件名装入 } } //3. 返回所有装起来的文件名 return allFileNames; } } //导入执行 import com.zh.Files.Filter; Files.getAllFileNames("F://", new Filter() { @Override public boolean accept(String filename) { return filename.contains("类"); } });
-
回调
public class Networks { //接口 public interface Block { void success(Object response); void failure(); } public static void get(String url,Block callback) { //1. 根据url发送一个异步请求(开启一条新的线程) //2. 请求完毕,返回结果 boolean result = true; if(result) { Object response = null; callback.success(response); }else { callback.failure(); } } } public static void main(String[] args) { Networks.get("http://www.baidu.com", new Block() { @Override public void success(Object response) { //请求成功 } @Override public void failure() { //请求失败 } }); }
-
排序
-
可以使用 JDK 自带的 java.util.Arrays 类对数组进行排序
Integer[] array = { 33, 22, 11, 77, 66, 99 }; Arrays.sort(array); //打印数组的字符串格式 // [11, 22, 33, 66, 77, 99] System.out.println(Arrays.toString(array)); //1. Arrays.sort这个方法首先将数组进行升序排列 Arrays.sort(array, new Comparator<Integer>() { @Override //2. 根据return这个返回值的正负来判断,然后重新排序; //如果返回值大于0,就认为o1>o2,把o1放右边,o2放左边 //不要受o2-o1的影响,里面可以写成常量,1,-1,0都行 //比如o2为77,o1为99,o2-o1= -22,return结果小于0,就认为o1小于o2,将99方左边,77放右边 public int compare(Integer o1, Integer o2) { return o2 - o1; } }); // [99, 77, 66, 33, 22, 11] System.out.println(Arrays.toString(array));
- Arrays.sort 默认是升序排列,把比较小的元素放左边,把比较大的元素放右边
- compare 的返回值
- 等于 0:
o1 == o2
- 大于 0:
o1 > o2
- 小于 0:
o1 < o2
- 等于 0:
- Comparable(在泛型中讲) vs Comparator
- 如果数组元素本身具备可比较性(实现了 java.util.Comparable 接口)
- 可以直接使用 Arrays.sort 方法进行排序
- java.util.Comparator 的存在意义?
- 可以在不修改类源代码的前提下,修改默认的比较方式(比如官方类、第三方类)
- 可以让一个类提供多种比较方式
- 如果数组元素本身具备可比较性(实现了 java.util.Comparable 接口)
-
Lambda Expression
Lambda 表达式是 Java 8 开始才有的语法
函数式接口(Functional Interface**)
- 概念:只包含1个抽象方法的接口
-
可以在接口上面加上
@FunctionalInterface
注解,表示它是一个函数式接口@FunctionalInterface public interface Testable { void test(int v); }
- 函数式接口中还可以有其他方法(非抽象方法):可以参考 Comparator接口
- 有且只有一个抽象方法的声明;
- 可以有多个静态方法的实现
- 可以有多个default方法(默认方法)
- 可以有多个Object的public的抽象方法
- 就是Object这个基础类已经实现的Public方法
- 也可以认为是默认方法
package com.zh; @FunctionalInterface public interface Eatable2 { //有且只有一个抽象方法 int calculate(int v1, int v2); //可以有多个Object的public的抽象方法 // equals toString 方法是Object类中已经实现的方法 boolean equals(Object obj); String toString(); }
- 总结:不管函数式接口中到底有多少个方法,有且只有一个抽象方法的声明,需要使用者进行实现,其余的方法一定都已经实现了。
Lambda表达式概念
-
Lambda表达式标准格式
(参数列表)->{ return 返回值; }
- 参数列表可以省略参数类型
- 当只有一条语句时:可以省略大括号、分号、return
- 当只有一个参数时:可以省略小括号
- 当没有参数时:不能省略小括号
- Lambda 只能访问 final 或者 有效 final 的局部变量(同匿名类)
- 当匿名类实现的是函数式接口时,可以使用 Lambda 表达式进行简化
- 就是匿名类实现接口,这个接口只有一个抽象方法(函数式接口)
- 注意:这里是用lambda进行简化,就是替换,可以达到相同的目的。但是Lambda 表达 不等于 匿名类实现的是函数式接口
- Lambda 实例
-
例1
//函数式接口 @FunctionalInterface public interface Calculator { int calculate(int v1, int v2); } //main函数 static void execute(int v1, int v2, Calculator c){ System.out.println(c.calculate(v1, v2)); } public static void main(String[] args) { execute(10, 20, (int v1, int v2) -> { return v1 + v2; }); // 30 execute(11, 22, (v1, v2) -> v1 + v2); // 33 }
-
上面案例过滤器的Lambda写法
Files.getAllFileNames("F://", (filename)->{ return filename.contains("类"); }); Files.getAllFileNames("F://", (filename)->filename.contains("类")); Files.getAllFileNames("F://", filename->filename.contains("类"));
-
匿名类 vs Lambda
- 尽管使用Lambda可以取代函数式接口的匿名类实现,但是二者并不相等
- Lambda 没有引入新的作用域
- 举例:
-
函数式接口Testable
@FunctionalInterface public interface Testable { void test(int v); }
-
使用Lambda替代匿名类
public class OuterClass { private int age = 1; public class InnerClass { private int age = 2; void inner() { // int v = 4; // error Testable t = v -> { System.out.println(v); // 3 //注意这两句: 没有引入新的作用域,就是在Lambda表达式内部执行的代码,去掉Lambda表达式执行结果是一样,因此下面两句代码都是2 System.out.println(age); // 2 System.out.println(this.age); // 2 System.out.println(InnerClass.this.age); // 2 System.out.println(OuterClass.this.age); // 1 }; t.test(3); //上面一堆Lambda表达式貌似等价于下面这一堆,因此上面再定义一个int v = 4;会报错!!! //int v = 3; //System.out.println(v); // 3 //System.out.println(age); // 2 //System.out.println(this.age); // 2 //System.out.println(InnerClass.this.age); // 2 //System.out.println(OuterClass.this.age); // 1 } } } //main函数 new OuterClass().new InnerClass().inner();
-
使用匿名类
public class OuterClass { private int age = 1; public class InnerClass { private int age = 2; void inner() { //不会报错 int v = 4; Testable t = new Testable() { @Override public void test(int v) { System.out.println(v); // 3 System.out.println(age); // 2 ,局部类,直接访问外部成员即使是private //这里会报错,this此时指的就是匿名类这个对象,二这个匿名对象并没有age成员,所以报错 // System.out.println(this.age); // error System.out.println(InnerClass.this.age); // 2 System.out.println(OuterClass.this.age); // 1 } }; t.test(3); } } }
-
常用函数式接口
- java.util.function 包中提供了很多常用的函数式接口
- Supplier、Consumer、Predicate、Function
- 特点:
- 跟OC的block很相似
- 函数式编程:实参传递函数的实现,函数的调用在方法实现中调用
Supplier
-
定义
@FunctionalInterface public interface Supplier<T> { //无参数 T get(); }
-
应用(注意理解!!)
-
有时使用Supplier传参,可以避免代码的浪费执行(有必要的时候再执行)
//需求:实现一个函数,传入2个字符串参数,返回第一个不为空的字符串 //特点:如果第一个参数不为空,则第二个参数直接不考虑 package com.zh; import java.util.function.Supplier; public class Main { public static void main(String[] args) { //打印:makeString Jack //从打印结果来看,首先是调用了makeString函数,然后才会调用getFirstNotEmptyString1 System.out.println(getFirstNotEmptyString1("Jack", makeString())); //打印:jack //从打印结果来看,发现第一个参数不为空,直接忽略了makeString System.out.println(getFirstNotEmptyString2("jack", ()->makeString()));//使用Lambda //使用匿名类,传参为函数实现 System.out.println(getFirstNotEmptyString2("jack", new Supplier<String>() { @Override public String get() { return makeString(); } })); } static String makeString() { System.out.println("makeString"); return String.format("%d %d %d", 1, 2, 3); } //方法1 static String getFirstNotEmptyString1(String s1, String s2) { if (s1 != null && s1.length() != 0) return s1; if (s2 != null && s2.length() != 0) return s2; return null; } //方法2 static String getFirstNotEmptyString2(String s1, Supplier<String> s2) { if (s1 != null && s1.length() != 0) return s1; if (s2 == null) return null; //方法调用在内部 String str = s2.get(); return (str != null && str.length() != 0) ? str : null; } }
-
特点:
- 第二个参数是一个函数参数,传值必须是匿名类实现或者Lambda表达式
- 就相当于OC中的传参是Block
- 延迟了代码的执行
- 使用场景,Supplier这就相当于一个block,调用已经实现,传参传递的是函数的实现
-
Consumer
-
定义
@FunctionalInterface public interface Consumer<T> { //有参数 void accept(T t); //该方法是默认方法,已经实现。返回值是一个Consumer default Consumer<T> andThen(Consumer<? super T> after) { //判断传入参数after不能为null Objects.requireNonNull(after); //accept(t)是调用当前对象的accept方法 //after.accept(t):是调用传进来的after的accept方法 return (T t) -> { accept(t); after.accept(t); }; }; }
-
应用
-
例1:
//hook:遍历数组元素,并对每个元素做一个操作 void forEach(int[] nums, Consumer<Integer> c) { if (nums == null || c == null) return; for (int n : nums) { //函数(block)调用在方法内部 c.accept(n); } } int[] nums = { 11, 33, 44, 88, 77, 66 }; //传参为函数(block)实现 forEach(nums, (n) -> { String result = ((n & 1) == 0) ? "偶数" : "奇数"; System.out.println(n + "是" + result); });
-
andThen
//hook:遍历数组元素,并对每个元素做一个操作 void forEach(int[] nums, Consumer<Integer> c1, Consumer<Integer> c2) { if (nums == null || c1 == null || c2 == null) return; for (int n : nums) { //2个函数先后执行 c1.andThen(c2).accept(n); } } int[] nums = { 11, 33, 44, 88, 77, 66 }; forEach(nums, (n) -> { String result = ((n & 1) == 0) ? "偶数" : "奇数"; System.out.println(n + "是" + result); }, (n) -> { String result = ((n % 3) == 0) ? "能" : "不能"; System.out.println(n + result + "被3整除"); });
-
Predicate
-
定义
@FunctionalInterface public interface Predicate<T> { //返回值为boolean boolean test(T t); default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (T t) -> test(t) && other.test(t); } default Predicate<T> negate(){ return (T t) -> !test(t); } default Predicate<T> or(Predicate<? super T> other){ Objects.requireNonNull(other); return (T t) -> test(t) || other.test(t); } //静态方法的实现 static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
-
应用
-
例1:
//把所有数组元素拼接起来,然后元素进行过滤 String join(int[] nums, Predicate<Integer> p) { if (nums == null || p == null) return null; StringBuilder sb = new StringBuilder(); for (int n : nums) { if (p.test(n)) { //函数调用 sb.append(n).append("_"); } } sb.deleteCharAt(sb.length() - 1); return sb.toString(); } int[] nums = { 11, 33, 44, 88, 77, 66 }; //函数实现 String str = join(nums, (n) -> (n & 1) == 0); // 44_88_66 System.out.println(str);
-
and
String join(int[] nums, Predicate<Integer> p1, Predicate<Integer> p2) { if (nums == null || p1 == null || p2 == null) return null; StringBuilder sb = new StringBuilder(); for (int n : nums) { if (p1.and(p2).test(n)) {//二者同时成立 sb.append(n).append("_"); } } sb.deleteCharAt(sb.length() - 1); return sb.toString(); } int[] nums = { 11, 33, 44, 88, 77, 66 }; String str = join(nums, (n) -> (n & 1) == 0, (n) -> (n % 3) == 0); // 66 System.out.println(str);
-
or
String join(int[] nums, Predicate<Integer> p1, Predicate<Integer> p2) { if (nums == null || p1 == null || p2 == null) return null; StringBuilder sb = new StringBuilder(); for (int n : nums) { if (p1.or(p2).test(n)) {//二者其中一个成立 sb.append(n).append("_"); } } sb.deleteCharAt(sb.length() - 1); return sb.toString(); } int[] nums = { 11, 33, 44, 88, 77, 66 }; String str = join(nums, (n) -> (n & 1) == 0, (n) -> (n % 3) == 0); // 33_44_88_66 System.out.println(str);
-
negate
String join(int[] nums, Predicate<Integer> p) { if (nums == null || p == null) return null; StringBuilder sb = new StringBuilder(); for (int n : nums) { if (p.negate().test(n)) { sb.append(n).append("_"); } } sb.deleteCharAt(sb.length() - 1); return sb.toString(); } int[] nums = { 11, 33, 44, 88, 77, 66 }; String str = join(nums, (n) -> (n & 1) == 0); // 11_33_77 System.out.println(str);
-
Function
-
定义
@FunctionalInterface public interface Function<T, R> { //参数类型、返回值类型,都是泛型 R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity() {return t -> t;} }
-
应用
-
例1:
//字符串转为Integer然后求和 int sum(String[] strs, Function<String, Integer> f) { if (strs == null || f == null) return 0; int result = 0; for (String str : strs) { result += f.apply(str); //字符串转为Integer然后累加 } return result; } String[] strs = { "12", "567", "666" }; int result = sum(strs, Integer::valueOf); System.out.println(result);
-
andThen
// 将所有数字的个位数加起来 //参数传递2个函数实现 int sum(String[] strs,Function<String, Integer> f1, Function<Integer, Integer> f2) { if (strs == null || f1 == null || f2 == null) return 0; int result = 0; for (String str : strs) { result += f1.andThen(f2).apply(str); } return result; } String[] strs = { "12", "567", "666" }; int result = sum(strs, Integer::valueOf, (i) -> i % 10); // 15 System.out.println(result);
-
compose
// 将所有数字的个位数加起来 int sum(String[] strs,Function<String, Integer> f1, Function<Integer, Integer> f2) { if (strs == null || f1 == null || f2 == null) return 0; int result = 0; for (String str : strs) { result += f2.compose(f1).apply(str); } return result; } String[] strs = { "12", "567", "666" }; int result = sum(strs, Integer::valueOf, (i) -> i % 10); // 15 System.out.println(result);
-
方法引用(Method Reference)
- 如果 Lambda 中的内容仅仅是调用某个方法,可以使用方法引用(Method Reference)来简化
- Lambda对匿名类实现简化,那方法引用是对Lambda的简化
种类 用法 引用静态方法 ClassName::staticMethodName 引用特定对象的实例方法 ObjectName::instanceMethodName 引用特定类型的任意对象的实例方法 ClassName::methodName 引用构造方法 ClassName::new 引用当前类中定义的实例方法 this::instanceMethodName 引用父类中定义的实例方法 super::instanceMethodName
-
引用类方法
@FunctionalInterface public interface Testable { int test(int v1, int v2); } Testable t1 = (v1, v2) -> Math.max(v1, v2); // 20 System.out.println(t1.test(10, 20)); Testable t2 = Math::max; // 20 System.out.println(t2.test(10, 20));
-
引用特定对象的实例方法
@FunctionalInterface public interface Testable { void test(int v); } public class Person { public void setAge(int age) { System.out.println( "Person - setAge - " + age); } } static void execute(Testable t, int v) { t.test(v); } // 10 execute(v -> System.out.println(v), 10); // Person - setAge - 10 execute(v -> new Person().setAge(v), 10); //这个方法println的调用这是特定对象System.out // 20 execute(System.out::println, 20); //这个方法setAge的调用这是特定对象Person对象 // Person - setAge - 20 execute(new Person()::setAge, 20);
-
引用特定类型的任意对象的实例方法
String[] strings = { "Jack", "james", "Apple", "abort" }; //compareToIgnoreCase这个方法的调用者是任意对象,不是特定的某个对象 Arrays.sort(strings, (s1, s2) -> s1.compareToIgnoreCase(s2)); // [abort, Apple, Jack, james] System.out.println(Arrays.toString(strings)); Arrays.sort(strings, String::compareToIgnoreCase); // [abort, Apple, Jack, james] System.out.println(Arrays.toString(strings));
-
引用构造方法
@FunctionalInterface public interface Testable { Object test(int v); } public class Person { public Person(int age) { System.out.println("Person - " + age); } } Testable t1 = v -> new Person(v); // Person - 18 // Person@816f27d System.out.println(t1.test(18)); //引用构造方法 Testable t2 = Person::new; // Person - 18 // Person@6ce253f1 System.out.println(t2.test(18));
-
引用数组的构造方法
Testable t1 = v -> new int[v]; // 3 System.out.println(((int[]) t1.test(3)).length); //引用数组的构造方法 Testable t2 = int[]::new; // 3 System.out.println(((int[]) t2.test(3)).length);
-
引用当前类中定义的实例方法
@FunctionalInterface public interface Testable { void test(int v); } public class Person { public void setAge(int age) { System.out.println("setAge - " + age); } public void show() { Testable t1 = v -> setAge(v); // setAge - 10 t1.test(10); //引用当前类中定义的实例方法 Testable t2 = this::setAge; // setAge - 10 t2.test(10); } }
-
引用父类中定义的实例方法
@FunctionalInterface public interface Testable { void test(int v); } public class Person { public void setAge(int age) { System.out.println( "Person - setAge - " + age); } } public class Student extends Person { public void setAge(int age) { System.out.println("Student - setAge - " + age); } public void show() { Testable t1 = v -> super.setAge(v); // Person - setAge - 10 t1.test(10); //引用父类中定义的实例方法 Testable t2 = super::setAge; // Person - setAge - 10 t2.test(10); } }
综合实例
<X, Y> void process(Iterable<X> eles, Predicate<X> tester, Function<X, Y> mapper, Consumer<Y> block) {
if (eles == null || tester == null) return;
if (mapper == null || block == null) return;
for (X ele : eles) {
if (!tester.test(ele)) continue;
Y data = mapper.apply(ele);
block.accept(data);
}
}
public class Person {
private String name; private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
List<Person> ps = new ArrayList<>();
ps.add(new Person("Jack", 20));
ps.add(new Person("Rose", 10));
ps.add(new Person("Kate", 15));
ps.add(new Person("Larry", 40));
process(ps, (p) -> p.getAge() >= 15 && p.getAge() <= 25, Person::toString, System.out::println);
// Person [name=Jack, age=20]
// Person [name=Kate, age=15]
List<Integer> is = new ArrayList<>();
is.add(11);
is.add(22);
is.add(33);
is.add(44);
process(is, (i) -> (i & 1) == 0, (i) -> "test_" + i, System.out::println);
// test_22
// test_44