文章目录
- 接口增强
- 默认方法
- 静态方法
- 函数式接口
- Supplier
- Consumer
- Function
- Predicate
- Optional 类
- 以前对null 的处理
- Optional的基本使用
- Optional的常用方法
- 方法引用
- 方法引用的格式
- 对象名::方法名
- 类名::静态方法名
- 类名::引用实例方法
- 类名::构造器
- 数组::构造器
接口增强
在JDK8之前,接口的结构如下:
interface 接口名{静态常量;抽象方法;
}
JDK8之后对接口做了增加,接口中可以有默认方法和静态方法:
interface 接口名{静态常量;抽象方法;默认方法;静态方法;
}
默认方法
为什么要增加默认方法?
在JDK8以前接口中只能有抽象方法和静态常量,会存在以下的问题:
如果接口中新增抽象方法,那么实现类都必须要实现这个抽象方法,非常不利于接口的扩展的
interface A{void test1();// 接口中新增抽象方法,所有实现类都需要重写这个方法,不利于接口的扩展void test2();
}class B implements A{@Overridepublic void test1() {}@Overridepublic void test2() {}
}class C implements A{@Overridepublic void test1() {}@Overridepublic void test2() {}
}
如果接口A新增了test3()方法,接口B、C都需要实现,也不符合开闭原则。
默认方法的格式
接口中默认方法的语法格式是:
interface 接口名{修饰符 default 返回值类型 方法名{方法体;}
}
默认的方法,实现类可实现可不实现,当然也可以通过实例调用默认方式。
interface A{void test1();// 接口中新增抽象方法,所有实现类都需要重写这个方法,不利于接口的扩展void test2();/*** 接口中定义的默认方法* @return*/public default String test3(){System.out.println("接口中的默认方法执行了...");return "hello";}
}class B implements A{@Overridepublic void test1() {}@Overridepublic void test2() {}@Overridepublic String test3() {System.out.println("B 实现类中重写了默认方法...");return "ok ...";}
}class C implements A{@Overridepublic void test1() {}@Overridepublic void test2() {}
}
静态方法
静态方法作用也是为了接口的扩展
静态方法的格式
interface 接口名{修饰符 static 返回值类型 方法名{方法体;}
}
静态方法是不可以被实现的,调用的话只能通过接口类型来实现: 接口名.静态方法名()。
interface A{void test1();// 接口中新增抽象方法,所有实现类都需要重写这个方法,不利于接口的扩展void test2();/*** 接口中定义的默认方法* @return*/public default String test3(){System.out.println("接口中的默认方法执行了...");return "hello";}/*** 接口中的静态方法* @return*/public static String test4(){System.out.println("接口中的静态方法....");return "Hello";}
}
函数式接口
使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名、抽象方法名,只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方法,JDK中提供了大量常用的函数式接口,主要是在 java.util.function 包中。
Supplier
Supplier是一个无参有返回值的接口,对于的Lambda表达式需要提供一个返回数据的类型。
@FunctionalInterface
public interface Supplier<T> {/*** Gets a result.** @return a result*/T get();
}
Supplier 函数式接口的使用
/*** */
public class SupplierTest {public static void main(String[] args) {fun1(()->{int arr[] = {22,33,55,66,44,99,10};// 计算出数组中的最大值Arrays.sort(arr);return arr[arr.length-1];});}private static void fun1(Supplier<Integer> supplier){// get() 是一个无参的有返回值的 抽象方法Integer max = supplier.get();System.out.println("max = " + max);}
}
Consumer
Consumer是有参无返回值得接口,Supplier接口是用来生产数据的,而Consumer接口是用来消费数据的,使用的时候需要指定一个泛型来定义参数类型
@FunctionalInterface
public interface Consumer<T> {/*** Performs this operation on the given argument.** @param t the input argument*/void accept(T t);
}
使用:将输入的数据统一转换为小写输出
public class ConsumerTest {public static void main(String[] args) {test(msg -> {System.out.println(msg + "-> 转换为小写:" + msg.toLowerCase());});}public static void test(Consumer<String> consumer){consumer.accept("Hello World");}
}
Function
Function是一个有参有返回值的接口,Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。
@FunctionalInterface
public interface Function<T, R> {/*** Applies this function to the given argument.** @param t the function argument* @return the function result*/R apply(T t);
}
使用:传递进入一个字符串返回一个数字
public class FunctionTest {public static void main(String[] args) {test(msg ->{return Integer.parseInt(msg);});}public static void test(Function<String,Integer> function){Integer apply = function.apply("666");System.out.println("apply = " + apply);}
}
Predicate
Predicate是一个有参且返回值为Boolean的接口
@FunctionalInterface
public interface Predicate<T> {/*** Evaluates this predicate on the given argument.** @param t the input argument* @return {@code true} if the input argument matches the predicate,* otherwise {@code false}*/boolean test(T t);
}
使用:
public class PredicateTest {public static void main(String[] args) {test(msg -> {return msg.length() > 3;});}private static void test(Predicate<String> predicate){boolean b = predicate.test("HelloWorld");System.out.println("b:" + b);}
}
在Predicate中的默认方法提供了逻辑关系操作 and、or、negate、isEquals方法
package com.bobo.jdk.fun;import java.util.function.Predicate;public class PredicateDefaultTest {public static void main(String[] args) {test(msg1 -> {return msg1.contains("H");},msg2 -> {return msg2.contains("W");});}private static void test(Predicate<String> p1,Predicate<String> p2){// p1 包含H 同时 p2 包含Wboolean bb1 = p1.and(p2).test("Hello");// p1 包含H 或者 p2 包含Wboolean bb2 = p1.or(p2).test("Hello");// p1 不包含Hboolean bb3 = p1.negate().test("Hello");System.out.println(bb1); // FALSESystem.out.println(bb2); // TRUESystem.out.println(bb3); // FALSE}
}
Optional 类
Java的Optional类是一个用于解决null安全问题的工具类。在Java 8中引入了这个类,它提供了一种更优雅、更安全的方式来处理可能为null的值。
以前对null 的处理
@Testpublic void test01(){String userName = null;if(userName != null){System.out.println("字符串的长度:" + userName.length());}else{System.out.println("字符串为空");}}
Optional的基本使用
Optional对象的创建方式
/*** Optional对象的创建方式*/@Testpublic void test02(){// 第一种方式 通过of方法 of方法是不支持null的Optional<String> op1 = Optional.of("zhangsan");//Optional<Object> op2 = Optional.of(null);// 第二种方式通过 ofNullable方法 支持nullOptional<String> op3 = Optional.ofNullable("lisi");Optional<Object> op4 = Optional.ofNullable(null);// 第三种方式 通过empty方法直接创建一个空的Optional对象Optional<Object> op5 = Optional.empty();}
Optional的常用方法
/*** Optional中的常用方法介绍* get(): 如果Optional有值则返回,否则抛出NoSuchElementException异常* get()通常和isPresent方法一块使用* isPresent():判断是否包含值,包含值返回true,不包含值返回false* orElse(T t):如果调用对象包含值,就返回该值,否则返回t* orElseGet(Supplier s):如果调用对象包含值,就返回该值,否则返回 Lambda表达式的返回值* ifPresent*/@Testpublic void test03(){Optional<String> op1 = Optional.of("zhangsan");Optional<String> op2 = Optional.empty();// 获取Optional中的值if(op1.isPresent()){String s1 = op1.get();System.out.println("用户名称:" +s1);}// isPresent()&get()简洁表达op1.ifPresent(s-> System.out.println("有值:" +s));if(op2.isPresent()){System.out.println(op2.get());}else{System.out.println("op2是一个空Optional对象");}String s3 = op1.orElse("李四");System.out.println(s3);String s5 = op2.orElseGet(()->{return "Hello";});System.out.println(s5);}
方法引用
为什么要用方法引用?
在使用Lambda表达式的时候,也会出现代码冗余的情况,比如:用Lambda表达式求一个数组的和
public class FunctionRefTest01 {public static void main(String[] args) {printMax(a->{// Lambda表达式中的代码和 getTotal中的代码冗余了int sum = 0;for (int i : a) {sum += i;}System.out.println("数组之和:" + sum);});}/*** 求数组中的所有元素的和* @param a*/public void getTotal(int a[]){int sum = 0;for (int i : a) {sum += i;}System.out.println("数组之和:" + sum);}private static void printMax(Consumer<int[]> consumer){int[] a= {10,20,30,40,50,60};consumer.accept(a);}
}
上述代码中在Lambda表达式中要执行的代码和getTotal方法中的代码是一样的,没有必要重写一份逻辑了,这时我们就可以“引用”重复代码
public class FunctionRefTest01 {public static void main(String[] args) {// :: 方法引用 也是JDK8中的新的语法printMax(FunctionRefTest02::getTotal);}
方法引用的格式
符号表示:::
符号说明:双冒号为方法引用运算符,而它所在的表达式被称为 方法引用
应用场景:如果Lambda表达式所要实现的方案,已经有其他方法存在相同的方案,那么则可以使用方法引用。
方法引用的注意事项:
- 被引用的方法,参数要和接口中的抽象方法的参数一样
- 当接口抽象方法有返回值时,被引用的方法也必须有返回值
方法引用在JDK8中使用是相当灵活的,有以下几种形式:
- instanceName::methodName 对象::方法名
- ClassName::staticMethodName 类名::静态方法
- ClassName::methodName 类名::普通方法
- ClassName::new 类名::new 调用的构造器
- TypeName[]::new String[]::new 调用数组的构造器
对象名::方法名
这是最常见的一种用法。如果一个类中的已经存在了一个成员方法,则可以通过对象名引用成员方法
public static void main(String[] args) {Date now = new Date();Supplier<Long> supplier = ()->{return now.getTime();};System.out.println(supplier.get());// 然后我们通过 方法引用 的方式来处理Supplier<Long> supplier1 = now::getTime;System.out.println(supplier1.get());}
类名::静态方法名
public class FunctionRefTest04 {public static void main(String[] args) {Supplier<Long> supplier1 = ()->{return System.currentTimeMillis();};System.out.println(supplier1.get());// 通过 方法引用 来实现Supplier<Long> supplier2 = System::currentTimeMillis;System.out.println(supplier2.get());}
}
类名::引用实例方法
Java面向对象中,类名只能调用静态方法,类名引用实例方法是有前提的,实际上是拿第一个参数作为方法的调用者
public class FunctionRefTest05 {public static void main(String[] args) {Function<String,Integer> function = (s)->{return s.length();};System.out.println(function.apply("hello"));// 通过方法引用来实现Function<String,Integer> function1 = String::length;System.out.println(function1.apply("hahahaha"));}
}
类名::构造器
由于构造器的名称和类名完全一致,所以构造器引用使用 ::new
的格式使用,
public class FunctionRefTest06 {public static void main(String[] args) {Supplier<Person> sup = ()->{return new Person();};System.out.println(sup.get());// 然后通过 方法引用来实现Supplier<Person> sup1 = Person::new;System.out.println(sup1.get());BiFunction<String,Integer,Person> function = Person::new;System.out.println(function.apply("张三",22));}
}
数组::构造器
public static void main(String[] args) {Function<Integer,String[]> fun1 = (len)->{return new String[len];};String[] a1 = fun1.apply(3);System.out.println("数组的长度是:" + a1.length);// 方法引用 的方式来调用数组的构造器Function<Integer,String[]> fun2 = String[]::new;String[] a2 = fun2.apply(5);System.out.println("数组的长度是:" + a2.length);}
方法引用是对Lambda表达式符合特定情况下的一种缩写方式,它使得我们的Lambda表达式更加的精简,也可以理解为lambda表达式的缩写形式,不过要注意的是方法引用只能引用已经存在的方法。