本博客关于JDK1.8新特性介绍的目录在这里,欢迎点击
在前面简单介绍了一些Lambda表达式得好处与语法,我们知道使用Lambda表达式(需要了解lambda点我)是需要配合函数式接口的,java8已经为我们定义好了4类内置函数式接口,这4类接口可以解决我们开发过程中绝大部分的问题。
首先看定义
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
总而言之,JDK1.8中 interface允许定义默认方法和静态方法,通过默认接口定义、Lambda表达式、方法引用、函数式接口带来了函数式编程,这些功能也必将改变java开发习惯
默认方法的语法: default关键字 methodName(参数列表) { 实现体 }
静态方法语法与类的静态方法类似,不同的是接口静态方法的修饰符只能是public。
假如有一个Animal接口其中有ear()、fly()和swim()方法,有一个鸟Bird和一个dog同时实现这个接口,代码如下:
Animal 接口
public interface Animal {
void eat();
void fly();
void swim();
}
Bird 实现
class Bird implements Animal{
@Override
public void eat() {
System.out.println("bird can eat");
}
@Override
public void fly() {
System.out.println("bird can fly");
}
@Override
public void swim() {
}
}
Dog实现
class Dog implements Animal{
@Override
public void eat() {
System.out.println("dog can fly");
}
@Override
public void fly() {
}
@Override
public void swim() {
System.out.println("dog can fly");
}
}
从上代码可以看到,因为Animal中定义了三个方法,所以所有实现它的类都要覆写这三个方法,在Bird类中,鸟会飞,不会游泳,但是又必须要实现swim()方法,Dog类不会飞,但是又必须要实现fly()方法。代码出现冗余。
假如现在又有了新的需求,需要在Animal接口中再增加一个sleep()方法,那么之前所有实现了Animal接口的方法势必都在再覆写sleep()方法,整个系统中可能会有很多地方需要同步修改,而此时,default方法和静态方法就显得尤为必要了。
改写上面的例子:
Animal 接口
public interface Animal {
default void eat() {
System.out.println("Animal can eat");
};
default void fly() {
System.out.println("bird can fly");
};
default void swim() {
System.out.println("dog can swim");
};
}
Bird实现
class Bird implements Animal{
}
Dog实现
class Dog implements Animal{
}
测试类
public static void main(String[] args) {
Bird bird = new Bird();
bird.fly();
Dog dog = new Dog();
dog.swim();
}
运行结果
bird can fly
dog can swim
从修改后代码可以看出,代码得到了复用,Animal实现类中也没有了冗余。
假如有一个Animal工厂接口,该接口中有一个静态方法create()专门生产不同的Animal,在JDK1.8后由于引入了Lambda表达式,使子类不用覆写该接口的create()方法也可以生产任意的Animal,代码如下:
public interface AnimalFactory {
static Animal create(Supplier supplier) {
return supplier.get();
}
}
//注:Supplier supplier = Bird::new; 不会创建对象,
//只是通过supplier变量传递构造方法,supplier .get()才是真正去创建对象
//后续会提到这个类和 ‘::new‘这个这个语法
测试类
public static void main(String[] args) {
Animal dog = AnimalFactory.create(Dog::new);
dog.swim();
Animal bird = AnimalFactory.create(Bird::new);
bird.fly();
}
运行结果
fishes can swim......
birds can fly...
如果一个接口实现类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略
假如一个类实现了两个接口,两个接口中都有同样的默认方法,则编译无法通过
方法引用需要使用 :: 关键字,四种形式方法引用如下:
引用静态方法:类名称 :: static 方法名称;
引用某个对象的方法:实例化对象 :: 普通方法;
引用特定类型的方法:特定类 :: 普通方法
引用构造方法:类名称 :: new
前面提到的‘Animal bird = AnimalFactory.create(Bird::new);’ 就是属于引用构造方法,那我们再实现个几个例子,来测试一下。
引用静态方法
@FunctionalInterface
interface Inter{
public R zhuanhuan(P p);
}
public class testmain1 {
public static void main(String args[]){
Inter msg1 = String::valueOf;//引用Stirng的静态方法valueOf
String str = msg1.zhuanhuan(3000);
System.out.println("msg1:" + str); //msg1:3000
//原始方法
Inter msg2 = new Inter() {
public String zhuanhuan(Integer p) {
return String.valueOf(p);
}
};
System.out.println("msg2:" + str); //msg2:3000
}
}
//通过 Inter msg = String::valueOf;
//让Inter的R方法拥有了valueOf的功能,将String.valueOf()方法变为了Inter接口里的R()方法
@FunctionalInterface
作用:表明该接口是函数式接口,若该接口不符合函数式接口规范,则会提示> 错误信息。若不加该注解不影响程序。
~~
函数式接口,只能有一个 abstract 方法,可以包含多个默认方法、静态方法。
函数式接口可以包含Object里public的方法,这些方法不会计入 abstract 方法> 中,虽然它们是 abstract 方法。
因为任何一个函数式接口的实现,默认都继承了Object类,包含了来自Object里> 对这些 abstract 方法的实现。
引用普通方法
引用String类的比较方法 public int compareTo(String anotherString);
interface Inter{
public int compare(P p1,P p2);
}
public class Test{
public static void main(String args[]){
Inter msg = String::compareTo;
System.out.println(msg.compare("A","B")); // -1
}
}
函数式接口
@FunctionalInterface
public interface AdditiveComputation {
V additive(N n1, N n2);
}
引用函数式接口的类
public class NumberOperation {
private N n1;
private N n2;
public NumberOperation(N n1, N n2) {
this.n1 = n1;
this.n2 = n2;
}
public V calc(AdditiveComputation calcInterface) {
V v = calcInterface.additive(n1, n2);
return v;
}
}
测试
public static void main(String[] args) {
AdditiveComputation calcInterface1 = (n, v) -> n + v;
AdditiveComputation calcInterface2 = new AdditiveComputation() {
@Override
public Integer additive(Integer n1, Integer n2) {
return n1 + n2 + 3;
}
};
AdditiveComputation calcInterface3 = (n, v) -> n * v;
System.out.println(calcInterface1.additive(1, 2));
System.out.println(calcInterface2.additive(1, 2));
System.out.println(calcInterface3.additive(1, 2));
}
运行结果
calcInterface1:3
calcInterface2:6
calcInterface2:2
5.1 Predicate
断言型接口,有输出,输出为Boolean
提供一个抽象方法test, 接受一个参数, 根据这个参数进行一些判断, 返回判断结果 true / false,提供几个默认的default方法, and, or, negate 用于进行组合判断,在流中被广泛使用
Predicate predicate = item -> "test".equals(item);
// 1. test 方法测试
System.out.println("1---> " + predicate.test("test"));
List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
// 2. Predicate 返回一个List中的偶数
// list.stream(), 表示将List作为流进行处理, filter()方法接收一个Predicate, toArray是将流转换成数组
Object[] result = list.stream().filter(t -> t % 2 == 0).toArray();
System.out.println("2---> " + Arrays.toString(result));
// 3. 测试Predicate的and方法, 打印list中大于3, 小于6的数字
Predicate predicate1 = t -> t > 3;
predicate1 = predicate1.and(t -> t < 6);
result = list.stream().filter(predicate1).toArray();
System.out.println("3---> " + Arrays.toString(result));
// 4. 测试Predicate的or方法, 打印list中小于3或大于5的数字
predicate1 = t -> t < 3;
predicate1 = predicate1.or(t -> t > 5);
result = list.stream().filter(predicate1).toArray();
System.out.println("4---> " + Arrays.toString(result));
// 5. 测试Predicate的negate方法, 返回list中大于等于3,小于等于5的数字, 即对场景4取反
result = list.stream().filter(predicate1.negate()).toArray();
System.out.println("5---> " + Arrays.toString(result));
5.2 consumer
消费型接口,有输入,但是没返回值
Stream stream = Stream.of(1,2,3,4,5);
Consumer consumer1 = (s) -> System.out.println(s);
stream.forEach(consumer1);
consumer1.accept(1);
List lisiList = new ArrayList<>();
Consumer consumer2 = x -> {
if (x.getName().equals("lisi")){
lisiList.add(x);
}
};
consumer2 = consumer2.andThen(
x -> lisiList.removeIf(y -> y.getAge() < 23)
);
Stream.of(
new Person(21,"zhangsan"),
new Person(22,"lisi"),
new Person(23,"wangwu"),
new Person(24,"wangwu"),
new Person(23,"lisi"),
new Person(26,"lisi"),
new Person(26,"zhangsan")
).forEach(consumer2);
System.out.println(JSON.toJSONString(lisiList));
运行结果
1
2
3
4
5
1
[{"age":22,"name":"lisi"},{"age":23,"name":"lisi"},{"age":26,"name":"lisi"}]
5.3 Functional
函数型接口,有输入,有输出
Function function = x -> {
Person p = new Person();
p.setName(x);
return p;
};
Person p1 = function.apply("我是老王");
System.out.println("p1:" + p1.getName());
Function functionA = x -> {
return x.length();
};
Function functionB = x -> {
Person p = new Person();
p.setAge(x);
return p;
};
Function functionC = functionB.compose(functionA);
Person p2 = functionC.apply("我是老李");
System.out.println("p2:" + p2);
Function functionD = functionA.andThen(functionB);
Person p3 = functionD.apply("我是老张");
System.out.println("p3:" + p3);
运行结果
p1:我是老王
p2:Person(name=null, age=4)
p3:Person(name=null, age=4)
compose先执行()里的函functionA的apply方法,然后将其返回值作为functionB的apply方法的入参,执行functionB的apply方法
andThen则相反
还有例如IntFunction、DoubleFunction、ToLongFunction等等要求入参和返回值得Function。根据函数名就能推断出具体含义。
供给型接口,无输入,有输出
个人理解具有以下几点好处:
传递构造方法,需要时调用get()方法获取对象
Supplier bird3 = Bird::new;
有参构造可以这么写:
Supplier person= () -> new Person(“小王”,11);
> 注: 还是重复一遍,该方式只适用于构造方法传递,需要用到时再创建对象,否则和正常创建对象并无任何区别
传递方法 。比如有个一个计算函数,只返回integer类型,下面两个测试函数求数组最小和最大值,如下
public static int Calculation(Supplier sup) {
return sup.get();
}
public static void main(String args[]){
int arr[] = {9, 6, 88, 5, 13, 2, 4};
int maxValue = Calculation(() -> {
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
return max;
});
System.out.println(maxValue);
int minValue = Calculation(() -> {
int min = arr[0];
for (int i = 0; i < arr.length; i++) {
if (min > arr[i]) {
min = arr[i];
}
}
return min;
});
System.out.println(minValue);
函数式接口就介绍到这里了,后续还有强大的Stream API的介绍。