Java8相比之前的Java新增了几个重要的功能:Lambda表达式、函数式接口、Stream流。下面介绍一下Java中常用的函数式接口
一、什么是函数式接口?
函数式接口是指只有一个抽象方法的接口,它有个专门的注解@FunctionalInterface,该注解主要特点有:
1、该注解只能标记在"有且仅有一个抽象方法"的接口上,表示函数式接口。
2、JDK8接口中的静态方法和默认方法,都不算是抽象方法。
3、接口默认继承 java.lang.Object,所以如果接口显示声明覆盖了Object中的方法,那么也不算抽象方法。
4、允许java.lang.Object中的public方法
5、该注解不是必须的,如果一个接口符合"函数式编程"定义,那么加不加该注解都没有影响。加上该注解能够 更好地让编译器进行检查,
如果编写的不是函数式接口,但是加上了@FunctionalInterface 那么编译器会报错。
二、使用方法
定义一个函数式接口,说明它的用法
/**
* 函数式接口注解
*/
@FunctionalInterface
public interface PersonInterface {
/**
* 1、仅有一个抽象方法
*/
void say();
/**
* 2、java.lang.Object中的方法不算
*/
@Override
boolean equals(Object var1);
/**
* 3、java8 接口才可以有默认的方法实现 前提是方法名称必须使用default关键字修饰
*/
default void defaultMethod() {
System.out.println("haha");
}
/**
* 4、静态方法
*/
static void staticMethod() {
}
}
三、为什么只能有一个抽象方法?
函数式接口主要是为lambda表达式服务的,只有一个抽象方法可以使代码更简洁。下面写代码说明
public static void main(String[] args) {
//上面的接口 通过Lambda表达式重新 say方法
PersonInterface inter = () -> System.out.println("我说什么好呢?");
inter.say();
//控制台输出: 我说什么好呢?
}
通过 函数式接口 + Lambda表达式 让代码看去变的简洁,而这里的关键点在于:
()->{}这样的方法觉得代表对say方法的重写,如果有多个方法编译器就 不知道重写哪个方法了,这就是只有一个抽象方法的原因。
四、Java8中的函数式接口
Java8中带有的函数式接口主要有4个:Function
、Consumer
、Predicate
、Supplier,它们的特点如下:
1、Supplier:
供给类型,没有入参有返回值,主要用来
提前定义可能返回的一个指定类型结果,等需要调用的时候再获取结果。代码如下:
@FunctionalInterface
public interface Supplier<T> {
/**
* 只有这一个抽象类
*/
T get();
}
Supplier
在JDK8中Optional对象有使用到
Optional.orElseGet(Supplier<? extends T>) //当this对象为null,就通过传入supplier创建一个T返回。
使用的栗子
public static void main(String[] args) {
Person son = null;
//先判断son是否为null,如果为不为null则返回当前对象,如果为null则返回新创建的对象
BrandDTO optional = Optional.ofNullable(son).orElseGet(() -> new Person());
}
2、Consumer
消费类型,有输入参数没有返回值。主要是对对象进行处理,代码如下:
@FunctionalInterface
public interface Consumer<T> {
/**
* 抽象方法:传入一个指定泛型的参数,无返回值
*/
void accept(T t);
/**
* 如同方法名字一样andThen,类似一种相加的功能(下面会举例说明)
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
使用的示例如下:
public static void main(String[] args) {
testConsumer();
testAndThen();
}
/**
* 一个简单的平方计算
*/
public static void testConsumer() {
//设置好Consumer实现方法
Consumer<Integer> square = x -> System.out.println("平方计算 : " + x * x);
//传入值
square.accept(2);
}
/**
* 定义3个Consumer并按顺序进行调用andThen方法
*/
public static void testAndThen() {
//当前值
Consumer<Integer> consumer1 = x -> System.out.println("当前值 : " + x);
//相加
Consumer<Integer> consumer2 = x -> { System.out.println("相加 : " + (x + x)); };
//相乘
Consumer<Integer> consumer3 = x -> System.out.println("相乘 : " + x * x);
//andThen拼接
consumer1.andThen(consumer2).andThen(consumer3).accept(1);
}
Java8中的Stream中的函数peek和forEach接收的参数就是Consumer。
public interface Iterable<T> {
//forEach方法传入的就是Consumer
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
3、Function
既有入参、又有返回值。这种函数式接口主要用来输入一个值、得到处理后的值,代码如下:
@FunctionalInterface
public interface Function<T, R> {
/**
* 抽象方法: 根据一个数据类型T加工得到一个数据类型R
*/
R apply(T t);
/**
* 组合函数,调用当前function之前调用
*/
default <V> java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* 组合函数,调用当前function之后调用
*/
default <V> java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* 静态方法,返回与原函数参数一致的结果。x=y
*/
static <T> java.util.function.Function<T, T> identity() {
return t -> t;
}
}
使用的栗子
public static void main(String[] args) {
applyTest();
andThenTest();
composeTest();
test();
}
/**
* 1、apply 示例
*/
private static void applyTest() {
//示例1:定义一个funciton,实现将String转换为Integer
Function<String, Integer> function = x -> Integer.parseInt(x);
Integer a = function.apply("100");
System.out.println(a.getClass());
// 结果:class java.lang.Integer
}
/**
* 2、andThen 示例
*/
private static void andThenTest() {
//示例2:使用andThen() 实现一个函数 y=10x + 10;
//先执行 10 * x
Function<Integer, Integer> function2 = x -> 10 * x;
//通过andThen在执行 这里的x就等于上面的10 * x的值
function2 = function2.andThen(x -> x + 10);
System.out.println(function2.apply(2));
//结果:30
}
/**
* 3、compose 示例
*/
private static void composeTest() {
//示例3:使用compose() 实现一个函数 y=(10+x)2;
Function<Integer, Integer> function3 = x -> x * 2;
//先执行 x+10 在执行(x+10)*2顺序与上面相反
function3 = function3.compose(x -> x + 10);
System.out.println(function3.apply(3));
//结果:26
}
/**
* 4、综合示例
*/
private static void test() {
//示例4:使用使用compose()、andThen()实现一个函数 y=(10+x)*2+10;
//执行第二步
Function<Integer, Integer> function4 = x -> x * 2;
//执行第一步
function4 = function4.compose(x -> x + 10);
//执行第三步
function4 = function4.andThen(x -> x + 10);
System.out.println(function4.apply(3));
//结果:36
}
Java8的Stream流中的map的参数就是Function,HashMap中的函数computeIfAbsent的参数也是Fucntion。代码如下:
1、V HashMap.computeIfAbsent(K , Function<K, V>) // 简化代码,如果指定的键尚未与值关联或与null关联,使用函数返回值替换。
2、<R> Stream<R> map(Function<? super T, ? extends R> mapper); // 转换流
4、Predicate
Predicate接口主要用来判断一个参数是否符合要求。代码如下:
@FunctionalInterface
public interface Predicate<T> {
/**
* 具体过滤操作 需要被子类实现.
* 用来处理参数T是否满足要求,可以理解为 条件A
*/
boolean test(T t);
/**
* 调用当前Predicate的test方法之后再去调用other的test方法,相当于进行两次判断
* 可理解为 条件A && 条件B
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* 对当前判断进行"!"操作,即取非操作,可理解为 ! 条件A
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* 对当前判断进行"||"操作,即取或操作,可以理解为 条件A ||条件B
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* 对当前操作进行"="操作,即取等操作,可以理解为 A == B
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
常用的栗子
public static void main(String[] args) {
/**
* 1、判断数字是否大于7
*/
//设置一个大于7的过滤条件
Predicate<Integer> predicate = x -> x > 7;
System.out.println(predicate.test(10)); //输出 true
System.out.println(predicate.test(6)); //输出 fasle
/**
* 2、大于7并且
*/
//在上面大于7的条件下,添加是偶数的条件
predicate = predicate.and(x -> x % 2 == 0);
System.out.println(predicate.test(6)); //输出 fasle
System.out.println(predicate.test(12)); //输出 true
System.out.println(predicate.test(13)); //输出 fasle
/**
* 3、add or 简化写法
*/
predicate = x -> x > 5 && x < 9;
System.out.println(predicate.test(10)); //输出 false
System.out.println(predicate.test(6)); //输出 true
}
Java8中的Stream的fitler的参数就是一种predicate。
五、总结
Java8中新增的函数式接口是lambda表达式、Stream流的基础,有了它编写的代码更加简洁。我们平时的代码开发也可以多用函数式来编写代码,既可以提高开发效率又可以使代码更简洁。