java8filter取反_「java8系列」神奇的函数式接⼝
前⾔
在上⼀篇Lambda的讲解中我们就提到过函数式接⼝,⽐如:Consumer consumer = (s) -> System.out.println(s);其中Consumer就是⼀个函数式接⼝。这⾥是通过Lambda表达式创建了⼀个函数式接⼝的对象。如果不知道什么是Lambda,请看《神秘的Lambda》。
函数式接⼝是什么?
有且只有⼀个抽象⽅法的接⼝被称为函数式接⼝,函数式接⼝适⽤于函数式编程的场景,Lambda就是Java中函数式编程的体现,可以使⽤Lambda表达式创建⼀个函数式接⼝的对象,⼀定要确保接⼝中有且只有⼀个抽象⽅法,这样Lambda才能顺利的进⾏推导。
@FunctionalInterface注解
与@Override 注解的作⽤类似,Java 8中专门为函数式接⼝引⼊了⼀个新的注解:@FunctionalInterface 。该注解可⽤于⼀个接⼝的定义上,⼀旦使⽤该注解来定义接⼝,编译器将会强制检查该接⼝是否确实有且仅有⼀个抽象⽅法,否则将会报错。但是这个注解不是必须的,只要符合函数式接⼝的定义,那么这个接⼝就是函数式接⼝。
static⽅法和default⽅法
实在不知道该在哪介绍这两个⽅法了,所以就穿插在这⾥了。
static⽅法:
java8中为接⼝新增了⼀项功能,定义⼀个或者多个静态⽅法。⽤法和普通的static⽅法⼀样,例如:
public interface Interface {
/**
* 静态⽅法
*/
static void staticMethod() {
System.out.println("static method");
}
}
注意:实现接⼝的类或者⼦接⼝不会继承接⼝中的静态⽅法。
default⽅法:
java8在接⼝中新增default⽅法,是为了在现有的类库中中新增功能⽽不影响他们的实现类,试想⼀下,如果不增加默认实现的话,接⼝的所有实现类都要实现⼀遍这个⽅法,这会出现兼容性问题,如果定义了默认实现的话,那么实现类直接调⽤就可以了,并不需要实现这个⽅法。default⽅法怎么定义?
public interface Interface {
/**
* default⽅法
*/
default void print() {
System.out.println("hello default");
}
}
注意:如果接⼝中的默认⽅法不能满⾜某个实现类需要,那么实现类可以覆盖默认⽅法。不⽤加default关键字,例如:
public class InterfaceImpl implements Interface {
@Override
public void print() {
System.out.println("hello default 2");
}
}
在函数式接⼝的定义中是只允许有⼀个抽象⽅法,但是可以有多个static⽅法和default⽅法。
⾃定义函数式接⼝
按照下⾯的格式定义,你也能写出函数式接⼝:
@FunctionalInterface
修饰符 interface 接⼝名称 {
返回值类型 ⽅法名称(可选参数信息);
// 其他⾮抽象⽅法内容
}
虽然@FunctionalInterface注解不是必须的,但是⾃定义函数式接⼝最好还是都加上,⼀是养成良好的编程习惯,⼆是防⽌他⼈修改,⼀看到这个注解就知道是函数式接⼝,避免他⼈往接⼝内添加抽象⽅法造成不必要的⿇烦。
@FunctionalInterface
public interface MyFunction {
void print(String s);
}
看上图是我⾃定义的⼀个函数式接⼝,那么这个接⼝的作⽤是什么呢?就是输出⼀串字符串,属于消费型接⼝,是模仿Consumer接⼝写的,只不过这个没有使⽤泛型,⽽是将参数具体类型化了,不知道Consumer没关系,下⾯会介绍到,其实java8中提供了很多常⽤的函数式接⼝,Consumer就是其中之⼀,⼀般情况下都不需要⾃⼰定义,直接使⽤就好了。那么怎么使⽤这个⾃定义的函数式接⼝呢?我们可以⽤函数式接⼝作为参数,调⽤时传递Lambda表达式。如果⼀个⽅法的参数是Lambda,那么这个参数的类型⼀定是函数式接⼝。例如:
public class MyFunctionTest {
public static void main(String[] args) {
String text = "试试⾃定义函数好使不";
printString(text, System.out::print);
}
private static void printString(String text, MyFunction myFunction) {
myFunction.print(text);
}
}
执⾏以后就会输出“试试⾃定义函数好使不”这句话,如果某天需求变了,我不想输出这句话了,想输出别的,那么直接替换text就好了。函数式编程是没有副作⽤的,最⼤的好处就是函数的内部是⽆状态的,既输⼊确定输出就确定。函数式编程还有更多好玩的套路,这就需要靠⼤家⾃⼰探索了。
常⽤函数式接⼝
Consumer:消费型接⼝
抽象⽅法: void accept(T t),接收⼀个参数进⾏消费,但⽆需返回结果。
使⽤⽅式:
Consumer consumer = System.out::println;
consumer.accept("hello function");
默认⽅法: andThen(Consumer
after),先消费然后在消费,先执⾏调⽤andThen接⼝的accept⽅法,然后在执⾏andThen⽅法参数after中的accept⽅法。
使⽤⽅式:
Consumer consumer1 = s -> System.out.print("车名:"+s.split(",")[0]);
Consumer consumer2 = s -> System.out.println("-->颜⾊:"+s.split(",")[1]);
String[] strings = {"保时捷,⽩⾊", "法拉利,红⾊"};
for (String string : strings) {
consumer1.andThen(consumer2).accept(string);
}
输出:车名:保时捷-->颜⾊:⽩⾊车名:法拉利-->颜⾊:红⾊
Supplier: 供给型接⼝
抽象⽅法:T get(),⽆参数,有返回值。
使⽤⽅式:
Supplier supplier = () -> "我要变的很有钱";
System.out.());
最后输出就是“我要变得很有钱”,这类接⼝适合提供数据的场景。
Function: 函数型接⼝
抽象⽅法: R apply(T t),传⼊⼀个参数,返回想要的结果。
使⽤⽅式:
Function function1 = e -> e * 6;
System.out.println(function1.apply(2));
很简单的⼀个乘法例⼦,显然最后输出是12。
默认⽅法:
compose(Function
before),先执⾏compose⽅法参数before中的apply⽅法,然后将执⾏结果传递给调⽤compose函数中的apply⽅法在执⾏。
使⽤⽅式:
Function function1 = e -> e * 2;
Function function2 = e -> e * e;
Integer apply2 = function1pose(function2).apply(3);
System.out.println(apply2);
还是举⼀个乘法的例⼦,compose⽅法执⾏流程是先执⾏function2的表达式也就是33=9,然后在将执⾏结果传给function1的表达式也就是92=18,所以最终的结果是18。
andThen(Function
after),先执⾏调⽤andThen函数的apply⽅法,然后在将执⾏结果传递给andThen⽅法after参数中的apply⽅法在执⾏。它和compose ⽅法整好是相反的执⾏顺序。
使⽤⽅式:
Function function1 = e -> e * 2;
Function function2 = e -> e * e;
Integer apply3 = function1.andThen(function2).apply(3);
System.out.println(apply3);
这⾥我们和compose⽅法使⽤⼀个例⼦,所以是⼀模⼀样的例⼦,由于⽅法的不同,执⾏顺序也就不相同,那么结果是⼤⼤不同的。andThen⽅法是先执⾏function1表达式,也就是32=6,然后在执⾏function2表达式也就是66=36。结果就是36。
静态⽅法:identity(),获取⼀个输⼊参数和返回结果相同的Function实例。
使⽤⽅式:
Function identity = Function.identity();
Integer apply = identity.apply(3);
System.out.println(apply);
平常没有遇到过使⽤这个⽅法的场景,总之这个⽅法的作⽤就是输⼊什么返回结果就是什么。
Predicate : 断⾔型接⼝
抽象⽅法: boolean test(T t),传⼊⼀个参数,返回⼀个布尔值。
使⽤⽅式:
Predicate predicate = t -> t > 0;
boolean test = st(1);
System.out.println(test);
当predicate函数调⽤test⽅法的时候,就会执⾏拿test⽅法的参数进⾏t -> t > 0的条件判断,1肯定是⼤于0的,最终结果为true。
默认⽅法:
and(Predicate
other),相当于逻辑运算符中的&&,当两个Predicate函数的返回结果都为true时才返回true。
使⽤⽅式:
lambda编程Predicate predicate1 = s -> s.length() > 0;
Predicate predicate2 = Objects::nonNull;
boolean test = predicate1.and(predicate2).test("&&测试");
System.out.println(test);
or(Predicate
other) ,相当于逻辑运算符中的||,当两个Predicate函数的返回结果有⼀个为true则返回true,否则返回false。
使⽤⽅式:
Predicate predicate1 = s -> false;
Predicate predicate2 = Objects::nonNull;
boolean test = predicate1.and(predicate2).test("||测试");
System.out.println(test);
negate(),这个⽅法的意思就是取反。
使⽤⽅式:
Predicate predicate = s -> s.length() > 0;
boolean result = ate().test("取反");
System.out.println(result);
很明显正常执⾏test⽅法的话应该为true,但是调⽤negate⽅法后就返回为false了。
静态⽅法:isEqual(Object targetRef),对当前操作进⾏"="操作,即取等操作,可以理解为 A == B。
使⽤⽅式:
boolean test1 = Predicate.isEqual("test").test("test");
boolean test2 = Predicate.isEqual("test").test("equal");
System.out.println(test1); //true
System.out.println(test2); //false
其他函数式接⼝
Bi类型接⼝
BiConsumer、BiFunction、BiPrediate 是 Consumer、Function、Predicate 的扩展,可以传⼊多个参数,没有 BiSupplier 是因为Supplier 没有⼊参。
操作基本数据类型的接⼝
IntConsumer、IntFunction、IntPredicate、IntSupplier、LongConsumer、LongFunction、LongPredicate、LongSupplier、DoubleConsumer、DoubleFunction、DoublePredicate、DoubleSupplier。其实常⽤的函数式接⼝就那四⼤接⼝Consumer、Function、Prediate、Supplier,其他的函数式接⼝就不⼀⼀列举了,有兴趣的可以去java.util.function这个包下详细的看。
⼤家看后⾟苦点个赞点个关注哦!后续还会后更多的博客。 如有错误,烦请指正。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论