小朋友, 好好学学lambda表达式吧!

简介: 小朋友, 好好学学lambda表达式吧!

为什么要使用Lambda表达式


先看几段Java8以前经常会遇到的代码:


创建线程并启动


// 创建线程
public class Worker implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            doWork();
        }
    }
}
// 启动线程
Worker w = new Worker();
new Thread(w).start();


比较数组


// 定义一个比较器
public class LengthComparator implements Comparator<String> {
 @Override
 public int compare(String first, String second) {
     return Integer.compare(first.length(), second.length());
 }
}
//对字符数组进行比较
Arrays.sort(words, new LengthComparator());


给按钮添加单击事件


public void onClick(Button button) {
  button.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
          System.out.println("button clicked.");
      }
  });
}


对于这三段代码,我们已经司空见惯了。


但他们的问题也很突出:就是噪声太多!想实现一个数组的比较功能,至少要写5行代码,但其中只有一行代码才是我们真正关注的!


Java复杂冗余的代码实现一直被程序员所诟病,好在随着JVM平台语言Scala的兴起以及函数式编程风格的风靡,让Oracle在Java的第8个系列版本中进行了革命性的变化,推出了一系列函数式编程风格的语法特性,比如Lambda表达式以及Stream。


如果采用Lambda表达式,上面三段代码的实现将会变得极为简洁。


创建线程并启动(采用Lambda版本)


new Thread(() -> {
for (int i = 0; i < 100; i++) {
    doWork();
}
}).start();


比较数组(采用Lambda版本)


Arrays.sort(words, (first, second) -> Integer.compare(first.length(), second.length())


给按钮添加单击事件(采用Lambda版本)


button.addActionListener((event) -> System.out.println("button clicked."));

怎么样?通过Lambda表达式,代码已经变得足够简洁,让你把关注点全部都放在业务代码上。


Lambda表达式的语法



格式:


(参数) -> 表达式


其中:


  1. 参数可以为0-n个。如果有多个参数,以逗号(,)分割。如果有一个参数,括号()可以省去;如果没有参数,括号()也不能省去。[这就有点不够纯粹了,比scala还是差了点!],参数前可以加类型名,但由于自动类型推导功能,可以省去。


  1. 表达式可以是一行表达式,也可以是多条语句。如果是多条语句,需要包裹在大括号{}中。


  1. 表达式不需要显示执行返回结果,它会从上下文中自动推导。 以下是一些例子:


一个参数


event -> System.out.println("button clicked.")


多个参数


(first, second) -> Integer.compare(first.length(), second.length()


0个参数


() -> System.out.println("what are you nongshalei?")


表达式块


() -> {for (int i = 0; i < 100; i++) {    doWork();}}


函数式接口


在Java8中新增加了一个注解: @FunctionalInterface,函数式接口。


什么是函数式接口呢?它包含了以下特征:


  • 接口中仅有一个抽象方法,但允许存在默认方法和静态方法。


  • @FunctionalInterface注解不是必须的,但建议最好加上,这样可以通过编译器来检查接口中是否仅存在一个抽象方法。


Lambda表达式的本质就是函数式接口的匿名实现。只是把原有的接口实现方式用一种更像函数式编程的语法表示出来。


Java8的java.util.function包已经内置了大量的函数式接口,如下所示:


image.png

从中可以看出:


  • 内置的函数式接口主要分四类:Supplier, Consumer, Function,Predicate。Operator是Function的一种特例。


  • 除了Supplier没有提供二元参数以外(这和java不支持多个返回值有关),其他三类都提供了二元入参。


以下是一个综合的例子:


public class FunctionalCase {
    public static void main(String[] args) {
        String words = "Hello, World";
        String lowerWords = changeWords(words, String::toLowerCase);
        System.out.println(lowerWords);
        String upperWords = changeWords(words, String::toUpperCase);
        System.out.println(upperWords);
        int count = wordsToInt(words, String::length);
        System.out.println(count);
        isSatisfy(words, w -> w.contains("hello"));
        String otherWords = appendWords(words, ()->{
            List<String> allWords = Arrays.asList("+abc", "->efg");
            return allWords.get(new Random().nextInt(2));
        });
        System.out.println(otherWords);
        consumeWords(words, w -> System.out.println(w.split(",")[0]));
    }
    public static String changeWords(String words, UnaryOperator<String> func) {
        return func.apply(words);
    }
    public static int wordsToInt(String words, Function<String, Integer> func) {
        return func.apply(words);
    }
    public static void isSatisfy(String words, Predicate<String> func) {
        if (func.test(words)) {
            System.out.println("test pass");
        } else {
            System.out.println("test failed.");
        }
    }
    public static String appendWords(String words, Supplier<String> func) {
        return words + func.get();
    }
    public static void consumeWords(String words, Consumer<String> func) {
        func.accept(words);
    }
}


如果觉得这些内置函数式接口还不够用的话,还可以自定义自己的函数式接口,以满足更多的需求。


方法引用


如果Lambda表达式已经有实现的方法了,则可以用方法引用进行简化。 方法引用的语法如下:


  • 对象::实例方法
  • 类::静态方法
  • 类::实例方法


这样前面提到的Lambda表达式:


event -> System.out.println(event)


则可以替换为:


System.out::println


另一个例子:


(x,y)->x.cgnoreCase(y)


可以替换为:


String::compareToIgnoreCase


注意:方法名后面是不能带参数的!可以写成System.out::println,但不能写成System.out::println(“hello”)


如果能获取到本实例的this参数,则可以直接用this::实例方法进行访问,对于父类指定方法,用super::实例方法进行访问。


下面是一个例子:


public class Greeter {
    public void greet() {
        String lowcaseStr = changeWords("Hello,World", this::lowercase);
        System.out.println(lowcaseStr);
    }
    public String lowercase(String word) {
        return word.toLowerCase();
    }
    public String changeWords(String words, UnaryOperator<String> func) {
        return func.apply(words);
    }
}
class ConcurrentGreeter extends Greeter {
    public void greet() {
        Thread thread = new Thread(super::greet);
        thread.start();
    }
    public static void main(String[] args) {
        new ConcurrentGreeter().greet();
    }
}


构造器引用


构造器引用和方法引用类似,只不过函数接口返回实例对象或者数组。 构造器引用的语法如下:


  • 类::new
  • 数组::new


举个例子:


List<String> labels = Arrays.asList("button1", "button2");
Stream<Button> stream = labels.stream().map(Button::new);
List<Button> buttons = stream.collect(Collectors.toList());


其中的labels.stream().map(Button::new)相当于labels.stream().map(label->new Button(label))

再看个数组类型的构造器引用的例子:


Button[] buttons = stream.toArray(Button[]::new);


把Stream直接转成了数组类型,这里用Button[]::new来标示数组类型。


变量作用域


先看一段代码:


public void repeatMsg(String text, int count) {
    Runnable r = () -> {
        for (int i = 0; i < count; i++) {
            System.out.println(text);
            Thread.yield();
        }
    };
}


一个lambda表达式一般由以下三部分组成:


  • 参数
  • 表达式
  • 自由变量


参数和表达式好理解。那自由变量是什么呢? 它就是在lambda表达式中引用的外部变量,比如上例中的text和count变量。


如果熟悉函数式编程的同学会发现,Lambda表达式其实就是”闭包”(closure)。只是Java8并未叫这个名字。 对于自由变量,如果Lambda表达式需要引用,是不允许发生修改的。


其实在Java的匿名内部类中,如果要引用外部变量,变量是需要声明为final的,虽然Lambda表达式的自由变量不用强制声明成final,但同样也是不允许修改的。


比如下面的代码:


public void repeatMsg(String text, int count) {
    Runnable r = () -> {
        while (count > 0) {
            count--;  // 错误,不能修改外部变量的值
            System.out.println(text);
        }
    };
}


另外,Lambda表达式中不允许声明一个和局部变量同名的参数或者局部变量。 比如下面的代码:


Path first = Paths.get("/usr/bin");
Comparator<String> comp = (first, second) -> Integer.compare(first.length(), second.length());
// 错误,变量first已经被定义


接口中的默认方法


先说说为什么要在Java8接口中新增默认方法吧。


比如Collection接口的设计人员针对集合的遍历新增加了一个forEach()方法,用它可以更简洁的遍历集合。 比如:


list.forEach(System.out::println());


但如果在接口中新增方法,按照传统的方法,Collection接口的自定义实现类都要实现forEach()方法,这对广大已有实现来说是无法接受的。


于是Java8的设计人员就想出了这个办法:在接口中新增加一个方法类型,叫默认方法,可以提供默认的方法实现,这样实现类如果不实现方法的话,可以默认使用默认方法中的实现。


一个使用例子:


public interface Person {
    long getId();
    default String getName() {
        return "jack";
    }
}


默认方法的加入,可以替代之前经典的接口和抽象类的设计方式,统一把抽象方法和默认实现都放在一个接口中定义。这估计也是从Scala的Trait偷师来的技能吧。


接口中的静态方法


除了默认方法,Java8还支持在接口中定义静态方法以及实现。


比如Java8之前,对于Path接口,一般都会定义一个Paths的工具类,通过静态方法实现接口的辅助方法。


接口中有了静态方法就好办了, 统一在一个接口中搞定!虽然这看上去破坏了接口原有的设计思想。


public interface Path{
  public static Path get(String first, String... more) {
    return FileSystem.getDefault().getPath(first, more);
  }
}


这样Paths类就没什么意义了~


小结


使用Lambda表达式后可以大幅减少冗余的模板式代码,使把更多注意力放在业务逻辑上,而不是复制一堆重复代码,  除非你在一个用代码行数来衡量工作量的公司,你觉得呢?


目录
相关文章
|
4天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
5753 8
|
2天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
1012 2
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
16天前
|
人工智能 自然语言处理 安全
阿里云千问办公 QwenWork详细介绍:产品核心能力、典型场景、价格及常见问题解答
千问办公是阿里云推出的一站式AI办公平台,主打"不止于对话,更注重交付",依托通义千问旗舰大模型,用户一句话即可完成数据分析、PPT生成、视频剪辑等复杂任务,直接输出可用成果。产品深度打通钉钉生态与企业OA,覆盖桌面端、网页端,提供企业标准版198元/人/月等多档订阅方案,新用户注册即赠2000积分,适配工程师、HR、财务等多职业办公场景,成为能动手干活的"全能AI同事"。
3228 9
|
3天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
428 2
|
15天前
|
IDE 开发工具
Qoder 上线 Sonus 模型,Computer Use 能力全面增强
Qoder国际版上线全新内置大模型Sonus(/ˈsoʊnəs/),全球领先,专精超长任务执行与电脑操作(Computer Use)。配合Qoder桌面端0.2.3版本,可自主完成编程、金融建模、科研及表格制作等复杂工作。现全面支持Qoder全系产品,效率提升3.2倍。
1806 8
Qoder 上线 Sonus 模型,Computer Use 能力全面增强
|
11天前
|
缓存 IDE Java
【保姆级】Android Studio下载、安装和汉化教程(2026最新)
Android Studio 是 Google 官方推出的免费 Android 应用开发集成环境,基于 IntelliJ IDEA,内置模拟器、调试器、性能分析及 Compose 界面工具,功能全面,文档丰富,是安卓开发首选工具。(239字)
1190 1

热门文章

最新文章