【JavaSE高级】Java8新特性 - Lambda表达式

Java8新特性 - Lambda表达式

Java是面向对象语言,除了原始数据类型之处,Java 中的所有内容都是一个对象。而在函数式语言中,我们只需要给函数分配变量,并将这个函数作为参数传递给其它函数就可实现特定的功能。JavaScript 就是功能编程语言的典范(闭包)。

Lambda 表达式的加入,使得 Java 拥有了函数式编程的能力。在其它语言中,Lambda 表达式的类型是一个函数;但在 Java 中,Lambda 表达式被表示为对象,因此它们必须绑定到被称为功能接口的特定对象类型。

Lambda表达式简介

Lambda 表达式是一个匿名函数(对于 Java 而言并不很准确,但这里我们不纠结这个问题)。简单来说,这是一种没有声明的方法,即没有访问修饰符,返回值声明和名称。

Lambda 表达式是一段可以传递的代码,将面向对象中的传递数据变成传递行为。

在仅使用一次方法的地方特别有用,方法定义很短。它为我们节省了,如包含类声明和编写单独方法的工作。

Java 中的 Lambda 表达式通常使用语法是(argument) -> (body),比如:

(arg1, arg2...) -> { body }

(type1 arg1, type2 arg2...) -> { body }
()->(variable), ->是lambda表达式的操作符,将参数和方法的具体逻辑分离,这个线程启动的时候执行的是->之后的代码片段

以下是 Lambda 表达式的一些示例:

(int a, int b) -> {  return a + b; }

() -> System.out.println("Hello World");

(String s) -> { System.out.println(s); }

() -> 42

() -> { return 3.1415 };

例如:

//传统写法
Runnable r = new Runnable(){
    @Override
    public void run(){
        System.out.println("hello world");
    }
}

//lambda表达式
Runnable r = ()->System.out.println("hello world");
//传统写法
func(int x, int y){
    return x + y;
}

//lambda表达式
int sum = (x, y) -> x + y;

Lambda表达式的重要特征

  • Lambda 表达式可以具有零个,一个或多个参数。
  • 可以显式声明参数的类型,也可以由编译器自动从上下文推断参数的类型。例如(int a)与刚才相同(a)
  • 参数用小括号括起来,用逗号分隔。例如(a, b)(int a, int b)(String a, int b, float c)
  • 空括号用于表示一组空的参数。例如 () -> 42
  • 当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a * a
  • Lambda 表达式的正文可以包含零条,一条或多条语句。
  • 如果 Lambda 表达式的正文只有一条语句,则大括号可不用写,且表达式的返回值类型要与匿名函数的返回类型相同。
  • 如果 Lambda 表达式的正文有一条以上的语句必须包含在大括号(代码块)中,且表达式的返回值类型要与匿名函数的返回类型相同。

Lambda表达式中的方法引用

使用 Lambda 表达式,我们已经看到代码可以变得非常简洁。

例如,要创建一个比较器,以下语法就足够了:

Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());

然后,使用类型推断:

Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());

但是,我们可以使上面的代码更具表现力和可读性吗?我们来看一下:

Comparator c = Comparator.comparing(Person::getAge);

使用 :: 运算符作为 Lambda 调用特定方法的缩写,并且拥有更好的可读性。

  • 使用方式:
  • 双冒号(::)操作符是 Java 中的方法引用。 当们使用一个方法的引用时,目标引用放在 :: 之前,目标引用提供的方法名称放在 :: 之后,即 目标引用::方法。比如:
Person::getAge;
  • Person 类中定义的方法 getAge 的方法引用。
  • 然后我们可以使用 Function 对象进行操作:
// 获取 getAge 方法的 Function 对象
Function<Person, Integer> getAge = Person::getAge;
// 传参数调用 getAge 方法
Integer age = getAge.apply(p);
  • 我们引用 getAge,然后将其应用于正确的参数。
  • 目标引用的参数类型是 Function<T,R>T 表示传入类型,R 表示返回类型。比如,表达式 person -> person.getAge();,传入参数是 person,返回值是 person.getAge(),那么方法引用 Person::getAge 就对应着 Function<Person,Integer> 类型。

Lambda表达式和函数式接口结合使用

函数式接口:声明一个抽象方法的接口,Java8提供了一个注解,@FunctionalInterfance,这样如果接口中声明的抽象方法多于或者少于一个就会报错。例如:

@FunctionalInterface
interface FunctionInterface{
    void fun();
}

使用步骤

  • 1、新建函数式接口。
  • 2、新建包含属性为函数接口的类。
  • 3、实现函数式接口。
  • 4、测试。

使用实例

  • 无参无返回值:
@FunctionalInterface
interface FunctionInterface{
    void fun(); //无参无返回值
}
//无参无返回值
class TestFunctionInterface{
    FunctionInterface f = new FunctionInterface(){
        @Override
        public void fun() {
            System.out.println("hello world");
        }
    };
    FunctionInterface ff = () -> System.out.println("lambda表达式调用func()");
}
public class TestDemo {
    public static void main(String[] args) {
        //无参无返回值
        new TestFunctionInterface().f.fun();
        new TestFunctionInterface().ff.fun();
    }
}
  • 有参无返回值:
@FunctionalInterface
interface FunctionInterface{
    void fun(String s); //有参无返回值
}
//有参无返回值
class TestFunctionInterface{
    FunctionInterface f = new FunctionInterface(){
        @Override
        public void fun(String s) {
            System.out.println("通过 "+s+" 实现hello world");
        }
    };
    FunctionInterface ff = (String s) -> System.out.println("lambda表达式调用func()");
}
public class TestDemo {
    public static void main(String[] args) {
        //有参无返回值
        new TestFunctionInterface().f.fun("匿名类");
        new TestFunctionInterface().ff.fun("lambda");
    }
}
  • 无参有返回值:
@FunctionalInterface
interface FunctionInterface{
    String fun(); //无参有返回值
}
//无参有返回值
class TestFunctionInterface{
    FunctionInterface f = new FunctionInterface(){
        @Override
        public String fun() {
            return "hello world";
        }
    };
    FunctionInterface ff = () -> "lambda表达式调用func()";
}
public class TestDemo {
    public static void main(String[] args) {
        //无参有返回值
        new TestFunctionInterface().f.fun();
        new TestFunctionInterface().ff.fun();
    }
}
  • 有参有返回值:
@FunctionalInterface
interface FunctionInterface{
    String fun(String s); //有参有返回值
}
//有参有返回值
class TestFunctionInterface{
    FunctionInterface f = new FunctionInterface(){
        @Override
        public String fun(String s) {
            return "通过 "+s+" 实现hello world";
        }
    };
    FunctionInterface ff = (String s) -> "lambda表达式调用func()";
}
public class TestDemo {
    public static void main(String[] args) {
        //有参有返回值
        new TestFunctionInterface().f.fun("匿名类");
        new TestFunctionInterface().ff.fun("lambda");
    }
}

Lambda表达式的实例

  • 线程初始化:
// Old way
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello world");
    }
}).start();

// New way
new Thread(
    () -> System.out.println("Hello world")
).start();
  • 事件处理:

事件处理可以用 Java 8 使用 Lambda 表达式来完成。以下代码显示了将 ActionListener 添加到 UI 组件的新旧方式:

// Old way
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Hello world");
    }
});

// New way
button.addActionListener( (e) -> {
        System.out.println("Hello world");
});
  • 字符串数组排序:

对给定的字符串数组按照指定的排序方法进行排序。

String[] strs = "write code with Lambda in JAVA8".split(" ");

// Old way
Arrays.sort(strs, new Comparator<String>{
    public int compare(String s1, String s2){
        return s1.toLowerCase().compareTo(s2.toLowerCase())
    }
})

// New way
Arrays.sort(strs, (s1, s2) -> s1.toLowerCase().compareTo(s2.toLowerCase()));
  • 遍例输出(方法引用):

输出给定数组的所有元素的简单代码。请注意,还有一种使用 Lambda 表达式的方式。

// old way
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for (Integer n : list) {
    System.out.println(n);
}

// 使用 -> 的 Lambda 表达式
list.forEach(n -> System.out.println(n));

// 使用 :: 的 Lambda 表达式
list.forEach(System.out::println);
  • 逻辑操作:

输出通过逻辑判断的数据。

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Main {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
        
        System.out.print("输出所有数字:");
        evaluate(list, (n) -> true);
        
        System.out.print("不输出:");
        evaluate(list, (n) -> false);
        
        System.out.print("输出偶数:");
        evaluate(list, (n) -> n % 2 == 0);
        
        System.out.print("输出奇数:");
        evaluate(list, (n) -> n % 2 == 1);
        
        System.out.print("输出大于 5 的数字:");
        evaluate(list, (n) -> n > 5);
    }
    
    public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
        for (Integer n : list) {
            if (predicate.test(n)) {
                System.out.print(n + " ");
            }
        }
        System.out.println();
    }
}
//运行结果:
输出所有数字:1 2 3 4 5 6 7 
不输出:
输出偶数:2 4 6 
输出奇数:1 3 5 7 
输出大于 5 的数字:6 7 

Lambda表达式和匿名类之间的区别

  • this 关键字。对于匿名类 this 关键字解析为匿名类,而对于 Lambda 表达式,this 关键字解析为包含写入 Lambda 的类。
  • 编译方式。Java 编译器编译 Lambda 表达式时,会将其转换为类的私有方法,再进行动态绑定。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值