Java Lambda 表达式入门

入门

lambda表达式最简单的作用就是用于简化创建匿名内部类对象,下面先看一下什么是命令模式

命令模式

 命令模式简而言之就是将方法方法的具体实现分离开来,只有当调用该方法时,才确定该方法的具体实现,或者说具体行为
 下面定义一个Command接口,定义一个process方法用来封装处理行为。

public interface Command {
    //用于封装处理行为
    void process(int element);
}

 再定义一个数组处理类ProcessArray,包含一个process()方法,该方法不能确定处理数组的具体实现,所以使用了一个Command参数,通过这个参数负责处理数组的具体实现

public class ProcessArray {
    public void process(int[] target, Command cmd){
        for(var t : target){
            cmd.process(t);
        }
    }
}

 通过一个Command接口就实现了ProcessArray类与处理数组具体实现的分离。调用ProcessArray类的process方法时,处理行为是实现Command接口的对象决定的。下面用匿名内部类来实现处理数组的不同方法。

public class CommandTest {
 public static void main(String[] args) {
        var pa = new ProcessArray();
        int[] target = {3, -4, 6, 4};
        //第一次处理数组,这里的Command 参数,使用匿名内部类传入一个对象
        pa.process(target, new Command() {
            @Override
            public void process(int element) {
                System.out.println("数组元素的平方是:"+ element * element);
            }
        });
        //第二次处理数组
        pa.process(target, new Command() {
            @Override
            public void process(int element) {
                System.out.println("数迭代输出数组元素:"+ element);
            }
        });
    }
}

输出结果如下图所示:
在这里插入图片描述
可以看到,通过匿名内部类实现了处理数组的不同效果。

使用Lambda表达式简化创建匿名内部类对象

 上面通过命令模式这个例子引出了匿名内部类的使用,而Lambda表达式可以简化匿名内部类创建对象的过程,可以使代码更简洁。
 上面的代码片段:

 pa.process(target, new Command() {
            @Override
            public void process(int element) {
                System.out.println("数组元素的平方是:"+ element * element);
            }
        });

可以使用Lambda表达式改写为如下代码片段:

pa.process(target, (int element) -> {
            System.out.println("数组元素的平方是:" + element * element);
        });

可以看到Lambda表达式由三部分组成:

  • 形参列表: (int element)
     可以省略形参类型,如果形参列表只有一个参数则可以省略括号也可以省略,代码片段如下:
//形参列表省略形参类型
pa.process(target, (element) -> {System.out.println("数组元素的平方是:" + element * element);});
//形参列表省略形参类型,一个参数可以省略括号
pa.process(target, element -> {System.out.println("数组元素的平方是:" + element * element);});
  • 箭头:->
  • 代码块:{}

 只有一条语句可以省略花括号,代码片段如下所示:

pa.process(target, element -> System.out.println("数组元素的平方是:" + element * element));

 只有一条return语句可以省略return,代码片段如下所示:
先定义一个Addable接口,封装一个add方法:

public interface Addable {
    int add(int a, int b);
}

 在CommandTest类中定义一个类方法test(),调用该方法需要一个Addable对象:

public class CommandTest {
 public static void main(String[] args) {
        var pa = new ProcessArray();
        int[] target = {3, -4, 6, 4};
        //第一次处理数组,这里的Command 参数,使用匿名内部类传入一个对象
        pa.process(target, new Command() {
            @Override
            public void process(int element) {
                System.out.println("数组元素的平方是:"+ element * element);
            }
        });
        //第二次处理数组
        pa.process(target, new Command() {
            @Override
            public void process(int element) {
                System.out.println("数迭代输出数组元素:"+ element);
            }
        });
        //只有一条return语句可以省略return
        test((a,b) -> a + b);
    }
 public static void test(Addable addable){
      System.out.println("5 + 3 ="+addable.add(5,3));
  }
}

总结

在这里插入图片描述


函数式接口

 上面的代码能正常编译,说明Lambda表达式会被当成一个任意类型的对象lambda表达式的类型被称作目标类型,其目标类型必须为函数式接口(functional interface)(只包含一个抽象方法的接口)。因为Lambda表达式的结果就是被当成对象,所以可以用来赋值。

@FunctionalInterface
public interface Runnable {
    void run();
}

Runnable是java本身的函数式接口,下面使用Lambda表达式创建一个Runnable对象


Runnable r = () -> {
        for (var i = 0; i < 100; i++){
            System.out.println();
        }
    };

总结

在这里插入图片描述


在Lambda表达式中使用var

 var的类型是由编译器推断的,所以使用Lambda表达式给var赋值时需要指明Lambda表达式的类型。

//要进行强制转换指明类型
var run = (Runnable)()->{
            for (var i = 0; i < 100; i++){
                System.out.println();
            }
        };

总结

在这里插入图片描述


方法引用与构造器引用

Lambda表达式的代码块如果只有一条代码,则在代码块中可以使用方法引用构造器引用。两种引用都需要使用两个英文冒号“::”。

  • 引用类方法

 先定义一个函数式接口Converter

@FunctionalInterface
interface Converter{
    Integer convert(String from);
}

 再使用Lambda表达式创建Converter对象,通过该对象调用convert方法

public class MethodRefer {
    public static void main(String[] args) {
        Converter converter1 = from -> Integer.valueOf(from);
        var val = converter1.convert("99");
        System.out.println(val);
    }
}

 由于converter1是由Lambda表达式创建的,所以convert()方法的执行体是Lambda表达式的代码块部分。
 上述代码块可以改写为方法引用

//函数式接口中抽象方法的全部参数传给该类方法作为参数
Converter converter1 = Integer::valueOf;
  • 引用特定对象的实例方法

 使用Lambda表达式创建Converter2对象

Converter converter2 = from -> "abc".indexOf(from);
var val1 = converter2.convert("c");

输出2
上述代码可以使用方法引用替换:

Converter converter2 = "abc"::indexOf;
var val1 = converter2.convert("c");
  • 引用某类对象的实例方法

 先定义一个函数式接口Mytest

interface MyTest {
    String test(String a, int b, int c);
}

使用Lambda表达式创建Mytest对象,通过该对象调用test方法

MyTest mt = (a, b, c) -> a.substring(b, c);
var str = mt.test("123",1,2);
System.out.println(str);

上述代码可以使用方法引用替换:

MyTest mt = String::substring;
var str = mt.test("123",1,2);
  • 引用构造器

先定义一个函数式接口YourTest

interface YouTest {
    JFrame win(String title);
}

使用Lambda表达式创建YourTest对象,通过该对象调用win方法

 YouTest yt = a -> new JFrame(a);
        var jf = yt.win("window");
        System.out.println(jf);

上述代码可以使用构造器引用替换:

//函数式接口中抽象方法的全部参数传给该构造器作为参数
        YouTest yt = JFrame::new;
        var jf = yt.win("window");
        System.out.println(jf);

总结

在这里插入图片描述


与匿名内部类的联系与区别

总结

在这里插入图片描述


posted @ 2019-09-06 19:00  消灭猕猴桃  阅读(137)  评论(0编辑  收藏  举报