Java11开发秘籍-十五、Java10 和 Java11 的编程新方法

作者: Java开发者

在本章中,我们将介绍以下配方:

  • 使用局部变量类型推断
  • 对 Lambda 参数使用局部变量语法

介绍

本章简要介绍了影响编码的新功能。许多其他语言,包括 JavaScript,都具有使用var关键字声明变量的功能(在 Java 中,它实际上是保留类型名,而不是关键字)。它有许多优点,但并非没有争议。如果过度使用,尤其是使用较短的非描述性标识符,可能会降低代码的可读性,增加的值可能会被代码模糊度的增加所淹没。

这就是为什么在下面的配方中,我们解释了引入保留var类型的原因。在其他情况下尽量避免使用var

使用局部变量类型推断

在本教程中,您将了解 Java 10 中引入的局部变量类型推断,它可以在哪里使用,以及它的局限性。

准备

局部变量类型推断是编译器使用表达式的正确端识别局部变量类型的能力。在 Java 中,使用var标识符声明具有推断类型的局部变量。例如:

var i = 42;       //int
var s = "42";     //String
var list1 = new ArrayList();          //ArrayList of Objects;
var list2 = new ArrayList<String>();  //ArrayList of Strings

前面每个变量的类型都可以清楚地识别。我们在评论中捕捉到了他们的类型。

注意,var不是一个关键字,而是一个标识符,具有特殊含义,作为局部变量声明的类型。

它确实节省了输入,并使代码不那么混乱,重复代码。让我们看看这个例子:

Map<Integer, List<String>> idToNames = new HashMap<>();
//...
for(Map.Entry<Integer, List<String>> e: idToNames.entrySet()){
    List<String> names = e.getValue();
    //...
}

这是实现这种循环的唯一方法。但从 Java 10 开始,可以按如下方式编写:

var idToNames = new HashMap<Integer, List<String>>();
//...
for(var e: idToNames.entrySet()){
    var names = e.getValue();
    //...
}

正如您所看到的,代码变得更加清晰,但使用更具描述性的变量名(如idToNamesnames)是有帮助的。嗯,不管怎样,这是有帮助的。但是如果不注意变量名,很容易使代码不容易理解。例如,请查看以下代码:

var names = getNames();

查看前一行,您不知道names变量是什么类型。将其更改为idToNames更容易猜测。然而,许多程序员并不这样做。他们更喜欢短变量名,并使用 IDE 上下文支持(在变量名后添加一个点)确定每个变量的类型。但归根结底,这只是风格和个人喜好的问题。

另一个潜在的问题来自这样一个事实,即如果不采取额外的措施,新样式可能会违反接口原则的封装和编码。例如,考虑这个接口和它的实现:

interface A {
    void m();
}
static class AImpl implements A {
    public void m()
    public void f()
}

注意,AImpl类比它实现的接口有更多的公共方法。创建AImpl对象的传统风格如下:

A a = new AImpl();
a.m();
//a.f();  //does not compile

这样,我们只公开接口中存在的方法,而新样式允许访问所有方法:

var a = new AImpl();
a.m();
a.f();

要将引用仅限于接口的方法,需要添加类型转换,如下所示:

var a = (A) new AImpl();
a.m();
//a.f();  //does not compile

因此,与许多功能强大的工具一样,新样式可以使代码更易于编写,可读性更高,或者,如果不特别注意,可读性更低,调试更困难。

怎么做。。。

可以通过以下方式使用局部变量类型:

  • 使用右侧初始值设定项:
    var i = 1; 
    var a = new int[2];
    var l = List.of(1, 2); 
    var c = "x".getClass(); 
    var o = new Object() ; 
    var x = (CharSequence & Comparable<String>) "x";
    

以下声明和分配是非法的,不会编译:

var e;                 // no initializer
var g = null;          // null type
var f = ;         // array initializer
var g = (g = 7);       // self reference is not allowed
var b = 2, c = 3.0;    // multiple declarators re not allowed
var d[] = new int[4];  // extra array dimension brackets
var f = () -> "hello"; // lambda requires an explicit target-type

通过扩展,在循环中使用初始值设定项:

for(var i = 0; i < 10; i++){
    //...
}

我们已经讨论过这个例子:

var idToNames = new HashMap<Integer, List<String>>();
//...
for(var e: idToNames.entrySet()){
    var names = e.getValue();
    //...
}
  • 作为匿名类引用:
    interface A {
    void m();
    }
    var aImpl = new A(){
    @Override
    public void m(){
    //...
    }
    };
    
  • 作为标识符:
    var var = 1;
    
  • 作为方法名称:
    public void var(int i){
    //...
    }
    

var不能用作类或接口名。

  • 作为包名:
    package com.packt.cookbook.var;
    

对 Lambda 参数使用局部变量语法

在本配方中,您将学习如何对 Lambda 参数使用局部变量语法(在上一配方中讨论),以及引入此功能的动机。它是在 Java11 中引入的。

准备

在 Java11 发布之前,有两种方法可以显式和隐式声明参数类型。以下是一个明确的版本:

BiFunction<Double, Integer, Double> f = (Double x, Integer y) -> x / y;
System.out.println(f.apply(3., 2));    //prints: 1.5

以下是隐式参数类型定义:

BiFunction<Double, Integer, Double> f = (x, y) -> x / y;
System.out.println(f.apply(3., 2));     //prints: 1.5

在前面的代码中,编译器根据接口定义计算参数的类型。

在 Java11 中,使用var标识符引入了另一种参数类型声明方式。

怎么做。。。

  1. 以下参数声明与 Java 11 之前的隐式声明完全相同:
    BiFunction<Double, Integer, Double> f = (var x, var y) -> x / y;
    System.out.println(f.apply(3., 2));       //prints: 1.5
    
  2. 新的局部变量样式语法允许我们添加注释,而无需明确定义参数类型:
    import org.jetbrains.annotations.NotNull;
    ...
    BiFunction<Double, Integer, Double> f = 
    (@NotNull var x, @NotNull var y) -> x / y;
    System.out.println(f.apply(3., 2));        //prints: 1.5
    

注释告诉处理代码的工具(例如 IDE)程序员的意图,因此它们可以在编译或执行过程中警告程序员,以防违反声明的意图。例如,我们尝试在 IntelliJ IDEA 中运行以下代码:

BiFunction<Double, Integer, Double> f = (x, y) -> x / y;
System.out.println(f.apply(null, 2));    

它在运行时以NullPointerException失败。然后,我们运行了以下代码(带注释):

BiFunction<Double, Integer, Double> f4 = 
           (@NotNull var x, @NotNull var y) -> x / y;
Double j = 3.;
Integer i = 2;
System.out.println(f4.apply(j, i)); 

结果如下:

Exception in thread "main" java.lang.IllegalArgumentException: 
Argument for @NotNull parameter 'x' of com/packt/cookbook/ch17_new_way/b_lambdas/Chapter15Var.lambda$main$4 must not be null

Lambda 表达式甚至没有执行。

  1. 如果当参数是具有很长名称的类的对象时,我们需要使用注释,那么在 Lambda 参数的情况下使用局部变量语法的优势就显而易见了。在 Java 11 之前,代码可能如下所示:
    BiFunction<SomeReallyLongClassName, 
    AnotherReallyLongClassName, Double> f4 = 
    (@NotNull SomeReallyLongClassName x, 
     @NotNull AnotherReallyLongClassName y) -> x.doSomething(y);
    

我们必须显式声明变量的类型,因为我们想添加注释,而以下隐式版本甚至无法编译:

BiFunction<SomeReallyLongClassName, 
   AnotherReallyLongClassName, Double> f4 = 
      (@NotNull x, @NotNull y) -> x.doSomething(y);

对于 Java 11,新语法允许我们使用var标识符使用隐式参数类型推断:

BiFunction<SomeReallyLongClassName, 
   AnotherReallyLongClassName, Double> f4 = 
      (@NotNull var x, @NotNull var y) -> x.doSomething(y);

这就是为 Lambda 参数的声明引入局部变量语法的优点和动机。

文章列表

更多推荐

更多
  • IOS开发者的AWS和DevOps指南-十、iOS 应用开发的持续交付渠道 Jenkins 管道公司,AWS 代码管道,摘要,Fastlane 测试阶段,AWS 设备场测试阶段,Fastlane 构建阶段,Fastlane 交付阶段,为 AWS 代码管道设置 Jenkins 环境,在 AWS 控制台上设置代码管
    Apache CN

  • IOS开发者的AWS和DevOps指南-九、将 AWS 设备群用于测试 AWS 设备群简介,为应用测试生成 ipa 包,设置设备场项目并安排测试运行,AWS 设备场 Jenkins 插件,使用 Jenkins 自动化 AWS 设备群测试,摘要,使用 AWS 控制台安排测试运行,使用 AWS CLI 计划测试
    Apache CN

  • IOS开发者的AWS和DevOps指南-八、使用 Fastlane 自动构建、测试和发布 使用 Fastlane 匹配和亚马逊 S3 设置代码签名,设置 Jenkins 环境,用 Fastlane 自动化测试和构建,自动发布到 App Store Connect,摘要,正在初始化 Fastlane 匹配,在亚马逊 S3 存储
    Apache CN

  • IOS开发者的AWS和DevOps指南-六、使用 AWS CodeCommit 的源代码管理 Git 基础,创建 AWS 代码提交存储库,在 AWS 代码提交中添加源代码,AWS 代码提交中分支,AWS 代码提交中的拉请求,摘要,Git 安装,初始化 Git 存储库,记录对 Git 存储库的更改,克隆和使用远程 Git 存储库,
    Apache CN

  • IOS开发者的AWS和DevOps指南-七、将 AWS CodeCommit 与 Jenkins 集成 Jenkins 代码提交插件,设置集成组件,配置插件,使用 AWS 代码提交源创建 Jenkins 作业,摘要,通过 AWS 控制台设置组件,通过 Terraform 设置组件,测试 AWS 代码提交插件, 当应用源代码存储在 AWS
    Apache CN

  • IOS开发者的AWS和DevOps指南-四、AWS 上的 macOS 服务器 Amazon EC2 Mac 服务器,部署 Amazon EC2 Mac 服务器,连接到 Amazon EC2 Mac 服务器,使用 Amazon CloudWatch 监控 EC2 Mac 服务器,清理 Amazon EC2 Mac
    Apache CN

  • IOS开发者的AWS和DevOps指南-五、在 macOS 实例上设置开发工具 增加 macOS 实例宗卷大小,设置 Xcode,陷害 Jenkins,建立 Fastlane,设置 GitLab,摘要,Xcode 命令行工具,供应 Jenkins 控制器,EC2 Mac 实例作为 Jenkins 构建代理,创建 G
    Apache CN

  • IOS开发者的AWS和DevOps指南-三、亚马逊网络服务(AWS)上的 DevOps 三、亚马逊网络服务AWS上的 DevOpsAWS 上的持续集成,AWS 上的连续交付,基础设施作为代码,监控和记录,摘要,AWS 代码提交,AWS 代码构建,AWS 程式码人工因素,AWS 代码部署,AWS 代码管道,AWS 云阵,AW
    Apache CN

  • IOS开发者的AWS和DevOps指南-二、从 Xcode 到 App Store Connect 标识符,应用商店连接,从 Xcode 上传构件,测试和发布,摘要,截图和应用详细信息,TestFlight 软件,应用提交, 在前一章中,我们看到了如何使用 Xcode 在物理 iPhone 设备和模拟器上构建和运行应用。我们将进一步探
    Apache CN

  • IOS开发者的AWS和DevOps指南-一、iOS 应用开发基础 开发要求,迅速发生的,摘要,苹果个人计算机,苹果开发者账户,Xcode 简介,创建应用,构建应用,Xcode 命令行工具, 为了开发 iOS 应用,苹果提供了几种工具和资源。iOS 应用可以用原生编程语言开发,如 Swift 或 Obj
    Apache CN

  • 近期文章

    更多
    文章目录

      推荐作者

      更多