Lanbda表达式java8新特性

时间: 2023-08-22 admin IT培训

Lanbda表达式  java8新特性

Lanbda表达式 java8新特性

  • 1. 函数式接口

  • 2. Lambda表达式使用

  • 3. Lambda表达式中方法引用与构造器引用

  • 4. Lambda表达式中this与super关键字

  • 5. Lambda表达式的目标类型及变量作用域问题

  • 6. 常用函数式接口

 

1. 函数式接口

要想了解Lambda表达式是什么, 就必须弄明白什么是函数式接口.

         对于只有一个抽象方法的接口,需要这种接口的对象时, 就可以提供一个lambda表达式. 这种接口称为函数式接口.

这段定义摘自 <java核心卷|> . 不过需要注意的是: 函数式接口可以包含多个默认方法, 类方法, 但只能声明一个抽象方法.

 

如果自己想定义一个函数式接口, 可以在接口上加上 @FunctionalInterface 注解 , 不过该注解对程序功能没有任何作用,它用于告诉编译器执行更严格检查(检查是否只有一个抽象方法,否则会报错).

以下是jdk1.8中的函数式接口举例:

@FunctionalInterface
public interface IntToLongFunction {/*** Applies this function to the given argument.** @param value the function argument* @return the function result*/long applyAsLong(int value);
}

   

2. Lambda表达式使用                                                                                                                         

 lambda表达式主要由三部分组成:

1. 形参列表:  形参列表允许省略形参类型. 如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略.

2. 箭头(->):  必须通过英文中画线号和大于符号组成.

3. 代码块: 如果代码只包含一条语句, Lambda表达式可以省略代码花括号. Lambda只有一条return语句,甚至可以省略return关键字.

示例:

/*** 函数式接口 : 方法无返回值, 无参数* @FunctionalInterface 声明是函数式接口*/
@FunctionalInterface
public interface Eatable {void taste();}public class LambdaTest {public static void main(String[] args) {/*** Eatable接口中taste()方法 无返回值, 无参数*  代码块中只有一条语句 可省略"{}"  无参数 不可省略"()"*    完整代码:  () -> {System.out.println("苹果味道不错!");};*/Eatable eatable = () -> System.out.println("苹果味道不错!");eatable.taste();}}
/*** 函数式接口  方法有参数 无返回值*/
public interface Flyable {void fly(String weather);}public class LambdaTest {public static void main(String[] args) {/*** Flyable接口中fly()方法 无返回值, 有参数*   有一个参数可以省略"()" 及参数类型*   代码块只有一行代码 可省略"{}"*   完整代码:  (String weather) -> { System.out.println("今天天气如何? " + weather);};*/Flyable flyable = weather -> System.out.println("今天天气如何? " + weather);flyable.fly("天气很好!");}}
/*** 函数式接口 有两个参数 有返回值*/
public interface Addable {int add(int a,int b);}public class LambdaTest {public static void main(String[] args) {/*** Addable接口中add()方法  有两个参数  有返回值*    方法中有两个参数, 所以不可省略"()" 但可省略参数类型*    代码块中只有一条返回语句  所以可省略"{}" 和 "return" 语句*    完整代码: (int a, int b) -> {return a+b ;};*/Addable addable = (a,b) ->a + b;int add = addable.add(1, 2);System.out.println(add);  // 3}}

其实看到这里,我们已经大致掌握了lambda表达式的语法规则了. 

那么究竟什么是lambda表达式呢? 

       lambda表达式是一个可传递的代码块,可以在以后执行一次或多次.   (java核心卷|)

当然lambda也可以看成是匿名内部类的一种简写形式. 当使用lambda表达式替代匿名内部类创建对象时, Lambda表达式的代码块会替代实现抽象方法的方法体, Lambda表达式就相当于一个匿名方法.

某个方法在使用Lambda表达式时, 会接收 实现了该函数式接口的某个类的对象. 这些对象和类的管理完全取决于具体实现, 与

使用传统的内部类相比, 这样可能会高效得多.

 

3. Lambda表达式中方法引用与构造器引用

前面我们说到, 如果lambda表达式的代码块只有一条代码, 程序就可以省略lambda表达式中代码块的"{}".

不仅如此, 如果lambda表达式的代码块只有一条代码, 还可以在代码块中使用方法引用和构造器引用继续简化代码.

 

/*** Lambda 中方法引用和构造器引用**  主要有三种语法格式:*      对象::实例方法名*                        Employee employee = new Employee("张三",13,9000.00);*                        Supplier<String> supplier = () -> employee.getName();*                        Supplier<String> supplier2 = employee::getName;**       类::静态方法名*                        Comparator<Integer> com = (x,y) -> Integer.compare(x,y);*                        Comparator<Integer> com2 = Integer::compareTo;**       类::实例方法名*                        BiPredicate<String, String> bp = (x,y) -> x.equals(y);*                        BiPredicate<String,String> bp2 = String::equals;**  要求: 函数式接口的参数和返回值类型 要和 调用方法的参数返回值类型一致*       比如: Consumer<String>  void accept(String str)  和  System.out   void println(String x)**  构造器引用:*      ClassName::new   具体调用哪个构造器取决于调用方法有几个参数*      比如:*          Supplier<Employee> su = () -> Employee::new  // 调用的无参数构造器 因为Supplier 中 get()方法无参数**          Function<Integer,Employee> fun = (x) -> Employee::new // 调用了一个参数构造器, Function中 apply(T t)有参数*  数组引用:*      Function<Integer,String[]> fun = x -> new String[x];*      Function<Integer,String[]> fun2 = String[]::new*/

 

方法引用: 

 

 

    1.  引用类方法:

@FunctionalInterface
public interface Converter {// 该方法的目的为 将传入的 字符串 转为 数字类型   具体实现使用 Integer类的 parseInt()方法Integer convert(String from);
}public class LambdaTest {public static void main(String[] args) {// 普通lambda写法Converter converter = from -> Integer.parseInt(from);Integer convert = converter.convert("123");System.out.println(convert.getClass());// 引用类方法   // 注意: 此种写法 from参数会自动传入Integer.parseInt()方法中Converter converter1 = Integer::parseInt;Integer convert1 = converter1.convert("321");System.out.println(convert1.getClass());}}

  2.  引用特定对象的实例方法:

@FunctionalInterface
public interface Converter {Integer convert(String from);
}public class LambdaTest {public static void main(String[] args) {// 普通lambda写法  具体实现为查询指定字符串的位置Converter converter2 = from -> "fkit.org".indexOf(from);Integer it = converter2.convert("it");System.out.println(it);// 引用特定对象的实例方法 "fkit.org" 字符串为String 类的一个实例// 参数from 会自动传入 indexOf()方法中Converter converter3 = "fkit.org"::indexOf;Integer it1 = converter3.convert("it");System.out.println(it1);}}

 3. 引用某类对象的实例方法:

public interface MyTest {// 该方法为 根据 String int int 三个参数生成一个String 的返回值  a.substring(b,c)String test(String a,int b,int c);}public class LambdaTest {public static void main(String[] args) {// 普通lambda写法MyTest mt = (a,b,c) -> a.substring(b,c);String str = mt.test("Java I Love Yor", 2, 9);System.out.println(str);// 引用某类对象的实例方法// 注意这种写法的参数使用: 第一个参数会作为方法调用者,后面的参数会传入方法中MyTest mt1 = String::substring;String str1 = mt1.test("Java I Love You", 2, 9);System.out.println(str1);}}

  由此可见: 我们使用方法引用,有时是因为已经有现成的方法可以完成想要传递到其他代码的某个动作(就是说我们在lambda代码块中写的功能有现成的类或方法可以使用).

 

构造器引用: 构造器引用与方法引用类似, 只不过方法名为new.

public interface YourTest {JFrame win(String title);}public class LambdaTest {public static void main(String[] args) {// 普通lambda写法  返回一个对象YourTest yt = title -> new JFrame(title);JFrame title = yt.win("title");System.out.println(title);// 引用构造器 函数式接口中被实现的全部方法参数传给该构造器作为参数// title 将会被传入 new JFrame(title) 中YourTest yt1 = JFrame::new;JFrame title1 = yt1.win("title");System.out.println(title1);}}

 

4. Lambda表达式中this与super关键字

可以在方法引用使用this与super关键字.

使用this关键字时, 要注意,是指创建这个lambda表达式的方法的this参数.

public class Application {public void init(){ActionListener listener = event -> {// 注意 this.toString() 会调用Application对象的toString方法, 而不是ActionListener实例的toStringSystem.out.println(this.toString());};}}

上面的代码中, this.toString方法会调用Application对象的toString方法,不是ActionListener实例的方法,

同理: super 关键字 则为调用父类的方法.

 

5. Lambda表达式的目标类型及变量作用域问题

在上面的代码中我们都用了一种接口类型来接收表达式:  接口  接口名 = (参数) -> { ... }

那么我们是否可以使用Object类型类接收?  答案是不可以.  以下会报错:

Object obj = () -> {for(int i = 0 ; i<10 ; i++){System.out.println(i);}    }

Lambda的目标类型必须是明确的函数式接口.

Lambda只能为函数式接口创建对象.

但是可以使用函数式接口对Lambda表达式进行强制类型转换 ( 以下用强转为 Runnbale 接口示例 )

Object obj = (Runnable)() -> {for(int i = 0 ; i< 10 ; i++){System.out.println(i);}}

上面对Lambda表达式执行了强制类型转换,这样就可以确定表达式的目标类型为 Runnable 函数式接口.

 

Lambda表达式的作用域:

@FunctionalInterface
public interface Displayable {void display();default int add(int a , int b){return a + b;}}public class LambdaAndInner {private int age = 12;private static String name = "今天天气不错";public void test(){String book = "你好 , Java";Displayable dis = () ->{System.out.println("book局部变量为: " + book);  // 你好 , JavaSystem.out.println("外部类的age实例变量为: " + age);  // 12System.out.println("外部类的name类变量为: " + name);  // 今天天气不错//System.out.println(add(3,5));   // Lambda中不允许调用接口中定义的默认方法};dis.display();System.out.println(dis.add(3,5));}public static void main(String[] args) {LambdaAndInner lambda = new LambdaAndInner();lambda.test();}}

注意: 与内部类类似, 在Lambda中, 只能引用值不会改变的变量.

         Lambda表达式中获取的变量必须实际上是最终变量, 这个变量初始化后不会再为它赋新值.(相当于加了final,隐式final).

 

6. 常用函数式接口

/**** 常用函数式接口:*  Runnable       void run() ,*  Comparator<T>  int compare(T o1, T o2);** Java内置四种常见函数式接口*  Consumer<T>    消费型接口    void accept(T t) ,*  Supplier<T>    供给型接口    T get()*  Function<T,R>  函数型接口    R apply(T t)*  Predicate<T>   断言型接口    boolean test(T t)** 其他函数式接口*  BiFunction<T,U,R>   R apply(T t, U u)*  *  UnaryOperator<T>(Function 子接口)   T apply(T t)**  BinaryOperator<T>(BiFunction 子接口)  T apply(T t1, T t2)**  BigConsumer<T,U>  void accept(T t,U u)**  ToIntFunction<T>  ToLongFunction<T>  ToDoubleFunction<T> 参数为T  返回值分别为int long double**  IntFunction<R>   LongFunction<R>    DoubleFunction<R>  参数分别为int long double 返回值为 R*/