Java8的lambda表达式和函数式接口

1 为什么使用lambda表达式?

Lambda表达式允许将函数作为方法的参数,或者将code作为数据。使得代码简洁,可读性较高。

详情可参考官网 博客

2 Lambda表达式的格式

(attr1,attr2,.....->函数体

左边是参数,当参数为1个时,可以没有括号,比如:
e->e+2
当参数为0个时,用括号表示
()->System.out.println(“表达式”)

如果函数体为一行,那么就没有必须显示的使用return语句,比如,下面的两个代码是等价的:

Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );

如果函数体为多行,则需要将函数体用花括号”{ }”括起来*,表达式用分号结尾。*

Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );

通常参数的类型是由编译器推测出来的,比如:

public class test1 {

private static String e="letter";//成员变量

public static void main(String[] args) {
    Arrays.asList( "a", "b", "d" ).stream().forEach(attr->System.out.println(attr+"_"+ e ));
}

}
这里的attr被推测为String类型。

当然也可以显示的写出来:

public class test1 {
public static void main(String[] args) {
    String e="letter";//局部变量
    Arrays.asList( "a", "b", "d" ).stream().forEach((String  attr)->System.out.print( e ));
}
}

Lamda表达式可以引用类的成员变量和局部变量

3.函数式接口和Lambda表达式

函数式接口(也叫SAM接口,Single Abstract Method)的对象可以作为函数的参数,这样参数在引用时,可以以lambda表达式的方式出现。
每个函数式接口里仅允许有一个抽象方法,这个方法叫做函数式方法,lambda表达式的参数和返回类型必须和该函数的参数和返回类型保持一致。你无须去创建这个类,编译器会帮你实现。
例如:
//定义一个函数式接口getNewString ,其函数式方法是getNew

public interface getNewString {

public abstract String getNew(String old);

}

测试该函数式接口:

public class test1 {
    public static void main(String[] args) {
//定义1
        getNewString getmethod=(object)-> {return (object+"hello");};
        System.out.println(getmethod.getNew("Java8 "));
//定义2,当函数体仅有一行时,直接写表达式,不需要写return 语句。
getNewString getmethod2=(object)->  object+"Good Morning!";
System.out.println(getmethod.getNew("Java8 "));
//定义了函数式接口作为参数的函数,可以直接使用方法引用
Arrays.asList("1","2").stream().forEach(getmethod2::getNew);
    }
}   

从测试程序中的定义1和定义2可以看到,函数式接口的好处是可以做参数,并且函数体可以随意修改。

函数式接口仅允许有一个抽象方法,但是下面几种情况除外:

  • 接口里有一个默认方法

因为默认方法不是抽象方法,其有一个默认实现,所以是符合函数式接口的定义的:
如下代码不会报错:

    @FunctionalInterface
    interface GreetingService
    {
        void sayMessage(String message);

        default void doSomeMoreWork1()
        {
            // Method body        }

        default void doSomeMoreWork2()
        {
            // Method body        }
      }      
  • 函数式接口里允许定义静态方法

函数式接口里是可以包含静态方法,因为静态方法不能是抽象方法,是一个已经实现了的方法,所以是符合函数式接口的定义的;
如下代码不会报错:

  • @FunctionalInterface
    interface GreetingService
    {
    void sayMessage(String message);
    static void printHello(){
    System.out.println(“Hello”);
    }
    }
  • 函数式接口里允许定义java.lang.Object里的public方法

函数式接口里是可以包含Object里的public方法,这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法);因为任何一个函数式接口的实现,默认都继承了Object类,包含了来自java.lang.Object里对这些抽象方法的实现;
如下代码不会报错:

    @FunctionalInterface
    interface GreetingService  
    {
        void sayMessage(String message);

        @Override
        boolean equals(Object obj);
    }

因此,为了保证函数式接口定义准确,java8 提供了注解@FunctionalInterface
,这样当接口不正确时,在编译阶段会报错。例如:
@FunctionalInterface
public interface getNewString {

public abstract String getNew(String old);

}

  • Java中有哪些函数式接口呢?:

    java.lang.Runnable
    java.util.concurrent.Callable
    java.security.PrivilegedAction
    java.util.Comparator
    java.io.FileFilter
    java.nio.file.PathMatcher
    java.lang.reflect.InvocationHandler
    java.beans.PropertyChangeListener
    java.awt.event.ActionListener
    javax.swing.event.ChangeListener

java.util.function中定义了几组类型的函数式接口以及针对基本数据类型的子类。
Predicate — 传入一个参数,返回一个bool结果, 方法为boolean test(T t)
Consumer — 传入一个参数,无返回值,纯消费。 方法为void accept(T t)
Function — 传入一个参数,返回一个结果,方法为R apply(T t)
Supplier — 无参数传入,返回一个结果,方法为T get()
UnaryOperator — 一元操作符, 继承Function,传入参数的类型和返回类型相同。
BinaryOperator — 二元操作符, 传入的两个参数的类型和返回类型相同, 继承BiFunction
详细信息请参考官网

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页