Java语言自发布以来,从Java1.1一直到现在的Java14,Java通过不停的增加新功能,使得这门语言不断得到良好的提升。
其中比较具有重要里程碑的版本,如JDK5.0,提供给我们诸如增强for循环,可变参数列表,静态导入,枚举类型定义,自动装箱拆箱,泛型等一些列优秀的功能。
以及后来的jdk7.0,提供犹如二进制字面量,数字常量下划线,Switch运算中String类型的引入,try-with-resource的资源自动释放等,都给我们带来了很方便的操作和极大的便利。
接口方法
JDK1.8之前,接口中的方法,都必须是抽象方法。实现接口的类,必须实现接口中定义的每一个抽象方法。
由于JDK1.8的API,在已有的接口上,新增了很多的新方法,这种新增变化带来的问题,正如上述的情况一样,也会给使用这些接口的老用户带来很多的不便。
为了解决这个问题,JDK1.8中引入了一种新的机制:接口可以支持在声明方法的同时,提供实现。
主要通过两种方式可以完成这种操作:
- 默认方法
- 静态方法
默认方法
JDK1.8中,接口里面可以定义默认方法。
interface InterfaceName{
default returnType methodName(arg-list){ }
}
新时代的程序员:lambda表达式(方法引用)、链式编程、函数式接口、Stream流式计算
interface FirstInterface{
//传统定义,抽象方法,没有方法体。
voidbefore();
//默认方法
defaultvoidtest() {
System.out.println("Default method in FirstInterface");
}
}
classFirstClass implements FirstInterface{
//所有实现类必须实现接口中未实现的方法。
@Override
public void before() {
System.out.println("我是FirstInterface中的抽象方法,所有实现类必须实现!");
}
}
public class DefaultMethod {
publicstaticvoidmain(String[] args) {
FirstClass fc=newFirstClass();
fc.test(); //此处输出Default method in FirstInterface,对于默认方法,如果实现类中没有实现就是用默认的。
fc.before(); //此处输出我是FirstInterface中的抽象方法,所有实现类必须实现。
}
}
默认方法存在的两大优势:
- 可以让接口更优雅的升级,减少使用人员操作的负担不必随着接口方法增加,从而修改实现代码,因为默认方法在子类中可以不用实现
- 可以让实现类中省略很多不必要方法的空实现
方法调用的判断规则:
- 类中声明的方法优先级最高。类或父类中,声明的方法要高于任何默认方法的优先级
- 如果无法依据第一条进行判断,那么子接口的优先级更高例如,如果 B 接口继承了 A接口,那么 B 就比 A 更加具体,优先级更高所以,在上述例子中,B是子接口,优先级别更高,调用test方法后输出:defaut method test in B.
- 最后,如果还是无法判断,那么继承了多个接口的类,必须通过实现(重写)方法来确定方法的调用
静态方法
JDK1.8中,接口里面可以定义静态方法。
和类中定义的静态方法类似,接口中的静态方法可以直接通过接口名.静态方法名的形式进行访问。
语法:
interface InterfaceName{
static returnType methodName(arg-list){
//代码实现
}
}
访问:
InterfaceName.methodName();
注意,接口中的静态方法,只能使用当前接口的名字来调用
Lambda使用
虽然Lambda表达式在java中的实际意义,是对一个接口的实现,但并不是任何接口都可以使用Lambda表达式来进行实现。
原因也很简单,一个Lambda表达式,只是描述了一个函数的参数列表、函数主体、返回类型,那么它顶多是对接口中的一个抽象方法的实现,如果接口中有多个抽象方法呢?
很显然,这时候一个Lambda表达式是无法表示为这个接口的实现,因为无法实现接口中多个抽象方法。
所以,接口中==有且只有==一个抽象方法的时候,才可以使用Lambda表达式来对其进行实现。
方法引用:https://www.cnblogs.com/wuhenzhidu/p/10727065.html
函数式接口: 只有一个方法的接口;简化编程模型
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
// JDK1.5:泛型、枚举、反射
// JDK1.8:lambda表达式、链式编程、函数式接口、Stream流式计算
// 超级多FunctionalInterface
// 简化编程模型,在新版本的框架底层大量应用!
// foreach(消费者类的函数式接口)
JDK1.8中,针对函数式接口,新增了一个注解@FunctionalInterface
,用来检查被标注的接口,是不是一个函数式接口,如果不是,那么编译器会报错。
但是,该注解不是必须要用的,它只是会让编辑器帮我们检查一下而已,以免出现接口中抽象方法的个数不是1的情况。
例如,编译通过
@FunctionalInterface
interface Action{
int action(int a,int b);
}
例如,编译报错
@FunctionalInterface
interface Action{
int action(int a,int b);
int action();
}
Lambda语法
Lambda表达式的格式为:() -> {}
- ()表示需要表达的抽象方法参数列表
- ->后面跟的是函数主体
- {}函数主体,表达式的返回值,由这个函数主体中代码来决定
个人理解:Lambda表达式更专注抽象方法种的形参和行为,Lambda传入的参数类型可以省略但是传入的参数只是一个形参,并不是需要传入的实际数据,用于函数主体调用
代码测试:
Function
Function函数式接口
给Function<T ,R>接口传入两个参数,返回的参数为传入的第二个参数的类型,而第一个形参作为实参传入apply
方法。
package com.lxy.function;
import java.util.function.Function;
/**
* Function 函数型接口, 有一个输入参数,有一个输出
* 只要是 函数型接口 可以 用 lambda表达式简化
*/
public class Demo01 {
public static void main(String[] args) {
//Function<String,String> function = new Function<String,String>() {
// @Override
// public String apply(String str) {
// return str;
// }
// };
Function<String,String> function = (str)->{return str;};
System.out.println(function.apply("asd"));
}
}
package lambda;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
public class TestFunction {
public static void main(String[] args) {
String name = "zhangsan";
// 返回字符串的长度
int oper = oper(name, str -> str.length());
oper = oper(name, String::length);
// 返回字符串的对应下标
oper = oper(name, str -> (int) str.charAt(0));
// 遍历数组
Map<String ,Object> map = new HashMap();
map.put("张铁林",50);
map.put("陈一发",20);
map.put("健字号",52);
map.put("刘文治",33);
map.forEach(new BiConsumer<String, Object>() {
@Override
public void accept(String s, Object o) {
// 具体行为
}
});
map.forEach((str,obj) -> System.out.println(str+"->"+obj));
}
// 根据给定的字符串的,返回字符串对应具体条件的值.
public static int oper(String str, Function<String, Integer> function){
return function.apply(str);
}
}
Predicate
在使用该接口来做判断的时候,经常需要几个条件同时成立,或者其中一个条件成立,或者求反。
在这种情况下,除了可以在代码中选择使用&&,||,!之外,也可以分别使用这三个方法来代替。
- and()
- or()
- negate()
QQ截图20200908202109.png断定型接口:有一个输入参数,返回值只能是 布尔值!
package com.lxy.function;
import java.util.function.Predicate;
/**
* 断定型接口:有一个输入参数,返回值只能是 布尔值!
*/
public class Demo02 {
public static void main(String[] args) {
// 判断字符串是否为空
// Predicate<String> predicate = new Predicate<String>(){
// @Override
// public boolean test(String str) {
// return str.isEmpty();
// }
// };
Predicate<String> predicate = (str)->{return str.isEmpty(); };
System.out.println(predicate.test(""));
}
}
public class PredicateTest {
public static void main(String[] args) {
// 普通创建数组方法
Integer[] arrInteger = new Integer[]{1,20,15,8,9,31,40};
// jdk1.8创建数组
// 声明对此方法具体行为的引用,但并不是执行真正的方法
ActionDemo01 action = (size)->{return new Integer[size];};
action = Integer[]::new; // 与上面的效果是一样的,主要是上面new Integer[size],下面我们只需要声明就好了
action.arr(10);
// 条件1,数据大于10,第一种方式
List<Integer> filter = filter(arrInteger, new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer > 10;
}
});
// 第二种方式
filter = filter(arrInteger,(integer)->integer>10);
// 条件2,数据大于10,并且能被2整除,方式1
// 先写一个大于10断定式接口,之后再写一个能被二整除的断定式接口
Predicate<Integer> action1 = (integer)->integer>10;
Predicate<Integer> action2 = (integer)->integer%2==0;
// 将两个断定式接口组合
Predicate<Integer> p = action1.and(action2);
filter = filter(arrInteger,p);
// 方式2
filter = filter(arrInteger,(integer -> integer>10 && integer%2==0));
}
//定义一个方法,用来过滤数组中所有符合要求的数据。
public static List<Integer> filter(Integer[] val, Predicate<Integer> con) {
List<Integer> list = new ArrayList<>();
for(Integer i:val) {
if(con.test(i)) {
list.add(i);
}
}
return list;
}
}
// 声明接口
interface ActionDemo01{
Integer[] arr(Integer size);
}
Consumer
Consumer 消费型接口
QQ截图20200908202321.png
package com.lxy.function;
import java.util.function.Consumer;
/**
* Consumer 消费型接口: 只有输入,没有返回值
*/
public class Demo03 {
public static void main(String[] args) {
// Consumer<String> consumer = new Consumer<String>() {
// @Override
// public void accept(String str) {
// System.out.println(str);
// }
// };
Consumer<String> consumer = (str)->{System.out.println(str);};
consumer.accept("sdadasd");
}
}
public class TestConsumer {
public static void main(String[] args) {
Data[] datas = new Data[]{new Data("abc"),new Data("123"),new Data("这书评?"),new Data("放学了")};
// 打印结果,方式1
oper(datas, new Consumer<Data>() {
@Override
public void accept(Data data) {
System.out.println(data);
}
});
// 打印结果,方式2,因为规定了消费者接口种的泛型为Data所有传入的参数具有Data的行为
oper(datas,(data -> System.out.println(data.getVal())));
// 打印结果,方式3,注意返回的是接口!
Consumer<Data> con = System.out::println;
oper(datas,con);
}
// 提供一个方法,用来针对给定的一组Data种的每个值进行操作
public static void oper(Data[] data, Consumer<Data> consumer){
for (Data d : data){
consumer.accept(d);
}
}
}
class Data{
private String val;
public Data(String val) {
this.val = val;
}
public String getVal() {
return val;
}
public void setVal(String val) {
this.val = val;
}
}
QQ截图20200908202451.pngSupplier 供给型接口
package com.lxy.function;
import java.util.function.Supplier;
/**
* Supplier 供给型接口 没有参数,只有返回值
*/
public class Demo04 {
public static void main(String[] args) {
// Supplier supplier = new Supplier<Integer>() {
// @Override
// public Integer get() {
// System.out.println("get()");
// return 1024;
// }
// };
Supplier supplier = ()->{ return 1024; };
System.out.println(supplier.get());
}
}
public class TestSupplier {
public static void main(String[] args) {
//生成一个1--100以内的随机奇数
Supplier<Integer> sup = new Supplier<Integer>() {
@Override
public Integer get() {
int result ;
do {
result = (int)(Math.random()*100+1);
}while((result&1)==0);
return result;
}
};
int[] vals = consumer(()->{
int result ;
do {
result = (int)(Math.random()*100+1);
}while((result&1)==0); // 这个写发和result%2==0是一样的只不过效率更高
return result;
});
System.out.println(Arrays.toString(vals));
}
//定义一个方法,用来生成10个符合要求的1--100以内的随机数.
public static int[] consumer(Supplier<Integer> sup) {
int[] a = new int[10];
for(int i = 0;i<a.length;i++) {
a[i] = sup.get();
}
return a;
}
}
网友评论