lambda

Java Lambda 表达式

Java 8 新特性 Java 8 新特性


Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用 Lambda 表达式可以使代码变的更加简洁紧凑。

语法

lambda 表达式的语法格式如下:

(parameters) -> expression 或 (parameters) ->{ statements; }

以下是lambda表达式的重要特征:

  • **可选类型声明:**不需要声明参数类型,编译器可以统一识别参数值。
  • **可选的参数圆括号:**一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • **可选的大括号:**如果主体包含了一个语句,就不需要使用大括号。
  • **可选的返回关键字:**如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

Lambda 表达式实例

Lambda 表达式的简单例子:

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

在 Java8Tester.java 文件输入以下代码:

Java8Tester.java 文件

public class Java8Tester { public static void main(String args[]){ Java8Tester tester = new Java8Tester(); // 类型声明 MathOperation addition = (int a, int b) -> a + b; // 不用类型声明 MathOperation subtraction = (a, b) -> a - b; // 大括号中的返回语句 MathOperation multiplication = (int a, int b) -> { return a * b; }; // 没有大括号及返回语句 MathOperation division = (int a, int b) -> a / b; System.out.println("10 + 5 = " + tester.operate(10, 5, addition)); System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction)); System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication)); System.out.println("10 / 5 = " + tester.operate(10, 5, division)); // 不用括号 GreetingService greetService1 = message -> System.out.println("Hello " + message); // 用括号 GreetingService greetService2 = (message) -> System.out.println("Hello " + message); greetService1.sayMessage(“Runoob”); greetService2.sayMessage(“Google”); } interface MathOperation { int operation(int a, int b); } interface GreetingService { void sayMessage(String message); } private int operate(int a, int b, MathOperation mathOperation){ return mathOperation.operation(a, b); } }

执行以上脚本,输出结果为:

$ javac Java8Tester.java 
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google

使用 Lambda 表达式需要注意以下两点:

  • Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
  • Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。

变量作用域

lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。

在 Java8Tester.java 文件输入以下代码:

Java8Tester.java 文件

public class Java8Tester { final static String salutation = "Hello! "; public static void main(String args[]){ GreetingService greetService1 = message -> System.out.println(salutation + message); greetService1.sayMessage(“Runoob”); } interface GreetingService { void sayMessage(String message); } }

执行以上脚本,输出结果为:

$ javac Java8Tester.java 
$ java Java8Tester
Hello! Runoob

我们也可以直接在 lambda 表达式中访问外层的局部变量:

Java8Tester.java 文件

public class Java8Tester { public static void main(String args[]) { final int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); // 输出结果为 3 } public interface Converter<T1, T2> { void convert(int i); } }

lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)

int num = 1;  
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  
//报错信息:Local variable num defined in an enclosing scope must be final or effectively 
 final

在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

String first = "";  
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //编译会出错 

[Java 8 新特性]

7 篇笔记 写笔记

System.out.println("10 + 5 = " + tester.operate(10, 5, addition));

这里还有另外一种写法

System.out.println("10+5=" + addition.operation(10, 5));
interface MathOperation { int operation(int a, int b); }

此接口要求必须是函数式接口,如果其中有两个方法则lambda表达式会编译错误。但java8的新特性如许实现如下写法:

interface MathOperation {
    int operation(int a, int b);
        default int addition(int a, int b){
        return a+b;
    }
}

lambdas 实现 Runnable 接口

下面是使用 lambdas 来实现 Runnable 接口的示例:

// 1.1使用匿名内部类  
new Thread(new Runnable() {  
    @Override  
    public void run() {  
        System.out.println("Hello world !");  
    }  
}).start();  
  
// 1.2使用 lambda expression  
new Thread(() -> System.out.println("Hello world !")).start();  
  
// 2.1使用匿名内部类  
Runnable race1 = new Runnable() {  
    @Override  
    public void run() {  
        System.out.println("Hello world !");  
    }  
};  
  
// 2.2使用 lambda expression  
Runnable race2 = () -> System.out.println("Hello world !");  
   
// 直接调用 run 方法(没开新线程哦!)  
race1.run();  
race2.run();  

Runnable 的 lambda 表达式,使用块格式,将五行代码转换成单行语句。 接下来,在下一节中我们将使用 lambdas 对集合进行排序。

使用 Lambdas 排序集合

在 Java 中,Comparator 类被用来排序集合。 在下面的例子中,我们将根据球员的 name

surnamename 长度以及最后一个字母。 和前面的示例一样,先使用匿名内部类来排序,然后再使用 lambda 表达式精简我们的代码。

在第一个例子中,我们将根据name来排序list。 使用旧的方式,代码如下所示:

String[] players = {"Rafael Nadal", "Novak Djokovic",   
    "Stanislas Wawrinka", "David Ferrer",  
    "Roger Federer", "Andy Murray",  
    "Tomas Berdych", "Juan Martin Del Potro",  
    "Richard Gasquet", "John Isner"};  
   
// 1.1 使用匿名内部类根据 name 排序 players  
Arrays.sort(players, new Comparator<String>() {  
    @Override  
    public int compare(String s1, String s2) {  
        return (s1.compareTo(s2));  
    }  
});  

使用 lambdas,可以通过下面的代码实现同样的功能:

// 1.2 使用 lambda expression 排序 players  
Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));  
Arrays.sort(players, sortByName);  
  
// 1.3 也可以采用如下形式:  
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));  

其他的排序如下所示。 和上面的示例一样,代码分别通过匿名内部类和一些lambda表达式来实现Comparator :

// 1.1 使用匿名内部类根据 surname 排序 players  
Arrays.sort(players, new Comparator<String>() {  
    @Override  
    public int compare(String s1, String s2) {  
        return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));  
    }  
});  
  
// 1.2 使用 lambda expression 排序,根据 surname  
Comparator<String> sortBySurname = (String s1, String s2) ->   
    ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) );  
Arrays.sort(players, sortBySurname);  
  
// 1.3 或者这样,怀疑原作者是不是想错了,括号好多...  
Arrays.sort(players, (String s1, String s2) ->   
      ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) )   
    );  
  
// 2.1 使用匿名内部类根据 name lenght 排序 players  
Arrays.sort(players, new Comparator<String>() {  
    @Override  
    public int compare(String s1, String s2) {  
        return (s1.length() - s2.length());  
    }  
});  
  
// 2.2 使用 lambda expression 排序,根据 name lenght  
Comparator<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length());  
Arrays.sort(players, sortByNameLenght);  
  
// 2.3 or this  
Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));  
  
// 3.1 使用匿名内部类排序 players, 根据最后一个字母  
Arrays.sort(players, new Comparator<String>() {  
    @Override  
    public int compare(String s1, String s2) {  
        return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));  
    }  
});  
  
// 3.2 使用 lambda expression 排序,根据最后一个字母  
Comparator<String> sortByLastLetter =   
    (String s1, String s2) ->   
        (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));  
Arrays.sort(players, sortByLastLetter);  
  
// 3.3 or this  
Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1))); 

参考实例:

public class Test2 {

    public static void main(String[] args) {
        Test2 tester = new Test2();
        tester.testOperation();
    }

    public void testOperation() {
        /*
         * 
         * 1.不需要参数,返回值为5
         * () -> 5
         * 
         * 2.接收一个参数(数字类型),返回其2倍的值
         * x -> 2 * x
         * 
         * 3.接收2个参数(数字类型),返回他们的差值
         * (x,y) -> x - y
         * 
         * 4.接收2个int型整数,返回他们的和
         * (int x, int y) -> x + y
         * 
         * 5.接收一个String对象,并在控制台打印,不返回任何值(返回void)
         * (String s) -> System.out.print(s)
         * 
         * 6.接收2个参数(数字类型),第1个数字加1,第2个数字加2,再求积
         * (int x, int y) -> { x += 1; y += 2; return x * y }
         * 
         */

        /*
         * 
         * MathOperation接口有且只有一个抽象方法operation时,
         * 可以使用 MathOperation addition = (int a, int b) -> a + b;
         * addition为实现了接口的一个对象,
         * (int a, int b) -> a + b;作为一个函数实现了MathOperation的抽象方法operation;
         * 当MathOperation接口存在两个抽象方法时;
         * MathOperation addition = (int a, int b) -> a + b;在编写时报错;
         * 
         */

        // 传入的参数带有类型声明
        MathOperation addition = (int a, int b) -> a + b;

        // 不用带类型声明
        MathOperation subtraction = (a, b) -> a = b;

        // 大括号中带返回语句
        MathOperation multiplication = (int a, int b) -> {
            return a * b;
        };

        // 没有带大括号及返回语句
        MathOperation division = (int a, int b) -> a / b;
        System.out.println("2 * 3 = " + operate(2, 3, multiplication));
        System.out.println("6 / 3 = " + operate(6, 3, (int a, int b) -> a / b));

        // 输出结果,无返回值
        GreetingService greetService = (String message) ->
        System.out.println("hello " + message);
        greetService.sayMessage("world");
    }

    interface MathOperation {
        int operation(int a, int b);
        // int operation2(int a, int b);
        default int operation3(int a, int b) {
            return a + b;
        }
    }

    interface GreetingService {
        void sayMessage(String message);
    }

    private int operate(int a, int b, MathOperation mathOperation) {
        return mathOperation.operation(a, b);
    }
}

使用 Lambda 表达式对中文排序:

import java.text.Collator;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

public class Test {
    public static void main(String[] args) {    
        List<String> list = Arrays.asList("谷歌", "腾讯", "百度", "淘宝");    
        Collator collator = Collator.getInstance(Locale.CHINA);    
        list.sort((string1, string2) -> collator.compare(string1, string2));    
        System.out.println(list);
    }
}

以下实例反映出 lambda 不一定需要 final 关键字才可以访问外部变量。

public class LambdaTest {
    final static String hello = "Hello, ";
    private static String goodbye = "Goodbye, ";
    public static void main(String[] args) {
        GreetingMessage message1 = (String message) ->
        System.out.println(hello + message);
        GreetingMessage message2 = (message) ->
        {
            //goodbye = "the time is late, I'd go now, goodbye";
            goodbye += "the time is late, I'd go now, ";
            System.out.println(goodbye + message);
        };
        
        message1.sayMessage("张三");
        message2.sayMessage("李四");
    }
    
    interface GreetingMessage {
        void sayMessage(String message);
    }
}

运行程序后,结果 :

Hello, 张三
Goodbye, the time is late, I'd go now, 李四

Lambda

匿名内部类的格式:

new 父类或接口() {
    重写的方法; 
}

在匿名内部类中,有很多内容都是冗余的。

比如在使用匿名内部类实现多线程的代码中。

  • 因为 Thread 构造方法中需要传递 Runnable 接口类型的参数,所以我们不得不 new Runnable。
  • 因为要重写 Runnable 中的 run 方法,所以不得不写了public void run。

整个匿名内部类中最关键的东西是方法,方法中最关键的有前中后三点。

  • 前:参数。
  • 中:方法体
  • 后:返回值

最好的情况是只关注匿名内部类中最核心的这些内容(方法参数,方法体,返回值)如果使用Lambda表达式,可以只关注最核心的内容,Lambda 表达式是匿名内部类的简化写法。

Lambda 属于函数式编程思想。

  • 面向对象思想:怎么做。
  • 函数式编程思想:做什么。
public class Demo01Inner {
    public static void main(String[] args) {
        //使用匿名内部类的方式实现多线程。
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行了");
            }
        }).start();

        //使用Lambda表达式实现多线程
        new Thread(() -> System.out.println(Thread.currentThread().getName() + "执行了")).start();
    }
}

匿名内部类可以省去单独创建 .java 文件的操作。但是匿名内部类也是有缺点的,写法太冗余了,里面有很多多余的部分。

匿名内部类也有简化写法,匿名内部类的简化写法是 Lambda 表达式匿名内部类中最关键的内容是方法的参数,方法体,以及返回值,而在 Lambda 表达式中,关注的就是这三个关键的东西。

Lambda 表达式的标准格式:

(参数类型 参数名) -> {
    方法体;
    return 返回值;
}

格式解释:

  • 小括号中的参数和之前方法的参数写法一样,可以写任意个参数,如果多个参数,要使用逗号隔开。
  • -> 是一个运算符,表示指向性动作。
  • 大括号中的方法体以及 return 返回值的写法和之前方法的大括号中的写法一样。

Lambda 表达式是函数式编程思想。

函数式编程:可推导,就是可省略。

  • 因为在 Thread 构造方法中需要 Runnable 类型的参数,所以可以省略 new Runnable。
  • 因为 Runnable 中的只有一个抽象方法 run,所以重写的必然是这个 run 方法,所以可以省略 run 方法的声明部分(public void run)

Lambda 表达式可以省略面向对象中的一些条条框框,让我们只关注最核心的内容。

public class Demo02Lambda {
    public static void main(String[] args) {
        //实现多线程(单独创建.java)
        new Thread(new Task()).start();
        //使用匿名内部类的方式实现多线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行了");
            }
        }).start();
        //使用Lambda表达式完成多线程
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "执行了");
        }).start();
    }
}

匿名内部类与 Lambda 函数比较

public class Demo03Collections {
    public static void main(String[] args) {
        //创建集合
        List<Student> list = new ArrayList<>();
        //添加元素
        list.add(new Student("嫐", 20));
        list.add(new Student("嬲", 18));
        list.add(new Student("挊", 22));
        //使用比较器排序对集合中的学生对象根据年龄升序排序
        //Collections.sort(list, new Rule());

        //使用匿名内部类
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge();
            }
        });
       
        //使用Lambda表达式
        Collections.sort(list, (Student o1, Student o2) -> {
            return o1.getAge() - o2.getAge();
        });

        Collections.sort(list, (o1, o2) -> o1.getAge() - o2.getAge());

        System.out.println(list);

    }
}

Lambda 格式

Lambda 表达式的标准格式:

(参数类型 参数名) -> {
    方法体;
    return 返回值;
}

Lambda 表达式的省略规则:

  • \1. 小括号中的参数类型可以省略。
  • \2. 如果小括号中只有一个参数,那么可以省略小括号。
  • \3. 如果大括号中只有一条语句,那么可以省略大括号,return,分号。
public class Demo04SimpleLambda {
    //定义方法,使用接口当做参数
    public static void method(MyInterface m) {
        m.printStr("hello");
    }

    public static void main(String[] args) {
        //调用method方法,参数传递MyInterface实现类对象
        method(new MyInterface() {
            @Override
            public void printStr(String str) {
                System.out.println(str);
            }
        });
        //使用Lambda表达式的标准格式。
        method((String str) -> {
            System.out.println(str);
        });

        //1. 小括号中的参数类型可以省略。
        method((str) -> {
            System.out.println(str);
        });
        //2. 如果小括号中只有一个参数,那么可以省略小括号。
        method(str -> {
            System.out.println(str);
        });
        //3. 如果大括号中只有一条语句,那么可以省略大括号,return,分号。
        method(str -> System.out.println(str));
    }
}

Lambda 使用条件

Lambda 表达式的使用前提:

  • 必须有接口(不能是抽象类),接口中有且仅有一个需要被重写的抽象方法。
  • 必须支持上下文推导,要能够推导出来 Lambda 表达式表示的是哪个接口中的内容。

可以使用接口当做参数,然后传递 Lambda 表达式(常用),也可以将 Lambda 表达式赋值给一个接口类型的变量。

public class Demo05BeforeLambda {
    //使用接口当做参数
    public static void method(MyInterface m) {//m = s -> System.out.println(s)
        m.printStr("HELLO");
    }

    public static void main(String[] args) {
        //使用接口当做参数,然后传递Lambda表达式。
        //method(s -> System.out.println(s));

        //使用匿名内部类方式创建对象
        /*
        MyInterface m = new MyInterface() {
            @Override
            public void printStr(String str) {
                System.out.println(str);
            }
        };
        */

        MyInterface m = str -> System.out.println(str);
        m.printStr("Hello");
    }
}

测试实例:

public class LambdaDemo {

    final static String ST = "Hello!";
    String ss = "Hello!";

    public static void main(String[] args) {
        int c = 8;
        int d = 4;
        // int a = 10; 不允许声明一个与局部变量同名的参数或者局部变量。

        AddService add = (a, b) -> a + b;
        System.out.println(c + " + " + d + " = " + add.operation(c, d));

        add = (a, b) -> a - b;
        System.out.println(c + " - " + d + " = " + add.operation(c, d));

        add = (a, b) -> a * b;
        System.out.println(c + " * " + d + " = " + add.operation(c, d));

        add = (a, b) -> a / b;
        System.out.println(c + " / " + d + " = " + add.operation(c, d));

        add = (int a, int b) -> {
            return a + b;
        };
        System.out.println(c + " + " + d + " = " + add.operation(c, d));

        // 打印方法中可以获取ST,c,d 但是获取不到ss (且c,d不可更改,隐性的具有 final 的语义)
        GreetingService gs = str -> System.out.println(ST + str);
        gs.operation(String.valueOf(c));
    }

    /**
     * 实现方法可自定义
     */
    interface AddService {
        Integer operation(int a, int b);
    }

    interface GreetingService {
        void operation(String str);
    }
}
> a / b;
        System.out.println(c + " / " + d + " = " + add.operation(c, d));

        add = (int a, int b) -> {
            return a + b;
        };
        System.out.println(c + " + " + d + " = " + add.operation(c, d));

        // 打印方法中可以获取ST,c,d 但是获取不到ss (且c,d不可更改,隐性的具有 final 的语义)
        GreetingService gs = str -> System.out.println(ST + str);
        gs.operation(String.valueOf(c));
    }

    /**
     * 实现方法可自定义
     */
    interface AddService {
        Integer operation(int a, int b);
    }

    interface GreetingService {
        void operation(String str);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值