white and gray optical illusion

编写可靠的 Apache Groovy 代码:了解你的真值

在 Groovy 的世界里,“真或假”的概念并不像……那样简单直接
首页 » 博客 » 编写可靠的 Apache Groovy 代码:了解你的真值

在 Groovy 中,“真或假”的整个问题值得讨论。理解它能帮助你避免意外行为,编写清晰简洁的代码,有效地调试并有效地利用 Groovy 的特性。(如果你还没有安装 Groovy,请阅读这篇介绍,这是系列文章的其中一篇。)

Java 程序员应该意识到,对于像 intdouble 这样的原始类型,相等性的工作方式与类实例有所不同。

考虑以下 Java 程序

 1 import java.lang.*;
 2  public class Groovy06 {
 3    static public void main(String[] args) {
 4      String s1 = "Hi there";
 5      String s2 = "Hi " + "there";   
 6      System.out.println("s1.equals(s2) " + s1.equals(s2));
 7      System.out.println("s1 == s2 " + (s1 == s2));
 8      int i1 = 2;
 9      int i2 = 1 + 1;
10      System.out.println("i1.equals(i2) " + i1.equals(i2));
11      System.out.println("i1 == i2 " + (i1 == i2));
12    }
13  }

当你尝试编译这段代码时,你会得到这个错误

$ javac Groovy06.java
Groovy06.java:15: error: int cannot be dereferenced
System.out.println("i1.equals(i2) " + i1.equals(i2));
^
1 error
$

这个错误告诉你,由于 i1 是一个原始类型(在本例中是 int),你无法获得对它的引用来访问它的 equals() 方法——这就是原始类型的本质。如果你将 i1i2 的类型更改为 Integer

 1  import java.lang.*;
 2  public class Groovy06 {
 3    static public void main(String[] args) {
 4      String s1 = "Hi there";
 5      String s2 = "Hi " + "there";
 6      System.out.println("s1.equals(s2) " + s1.equals(s2));
 7      System.out.println("s1 == s2 " + (s1 == s2));
 8      Integer i1 = 2;
 9      Integer i2 = 1 + 1;
10      System.out.println("i1.equals(i2) " + i1.equals(i2));
11      System.out.println("i1 == i2 " + (i1 == i2));
12    }
13  }

现在你可以正常编译这个程序了。运行它会得到

$ java Groovy06
s1.equals(s2) true
s1 == s2 true
i1.equals(i2) true
i1 == i2 true
$

那么 .equals()== 之间有什么区别呢?再做一个更改来看看结果

 1  import java.lang.*;
 2  public class Groovy06 {
 3    static public void main(String[] args) {
 4      String s1 = new String("Hi there");
 5      String s2 = new String("Hi " + "there");
 6      System.out.println("s1.equals(s2) " + s1.equals(s2));
 7      System.out.println("s1 == s2 " + (s1 == s2));
 8      Integer i1 = new Integer(2);
 9      Integer i2 = new Integer(1 + 1);
10      System.out.println("i1.equals(i2) " + i1.equals(i2));
11      System.out.println("i1 == i2 " + (i1 == i2));
12    }
13  }

现在运行它

$ java Groovy06
s1.equals(s2) true
s1 == s2 false
i1.equals(i2) true
i1 == i2 false
$

在这里你可以看到,创建新实例意味着每个实例的值是相等的,但实例本身并不相等。更准确地说,s1s2 指的是不同的对象,i1i2 也是如此。碰巧的是,s1s2 包含相同的值,i1i2 也是如此。所以 .equals() 比较的是值,而 == 比较的是引用,在本例中,引用指向内存的不同部分。

我听说在 Java 程序的野外,存在一些细微的 bug,是由于程序员使用了 == 而不是 .equals() 造成的。我相信这一点。

Groovy 的设计者采取了不同的方法。在 Groovy 的世界里,== 符号完全等同于 .equals() 方法。在需要比较引用的情况下,Groovy 有符号 ===(以及用于否定引用相等的符号 !==)。

你可以编写一个小脚本来测试这一点,基于之前的 Java 示例

 1  String s1 = new String("Hi there")
 2  String s2 = new String("Hi " + "there")
 3  println "s1.equals(s2) ${s1.equals(s2)}"
 4  println "s1 == s2 ${s1 == s2}"
 5  println "s1 === s2 ${s1 === s2}"
 6  int i1 = new Integer(2)
 7  int i2 = new Integer(1 + 1)
 8  println "i1.equals(i2) ${i1.equals(i2)}"
 9  println "i1 == i2 ${i1 == i2}"
10  println "i1 === i2 ${i1 === i2}"

现在运行它

$ groovy Groovy06.groovy
s1.equals(s2) true
s1 == s2 true
s1 === s2 false
i1.equals(i2) true
i1 == i2 true
i1 === i2 true
$

我非常喜欢 Groovy 使用 === 运算符来测试引用的相等性,以及 == 被评估为 .equals() 的方式。这对我来说感觉更安全。值得在此强调的是,Groovy 中没有任何原始类型。因此,如果 ==.equals() 不同,那就意味着你必须记住在看起来像原始类型的东西上使用 .equals()

还需要强调的是,使用 Groovy 编译和运行 Java 程序会得到与 Java 不同的答案。对于将 Java 代码迁移到 Groovy 的人(或反之亦然!),记住这一点非常重要。

到目前为止,除了 Java 和 Groovy 中 == 的区别之外,truefalse 看起来非常相似。但是 Groovy 引入了一个被称为 “Groovy 真值” 的概念,这个概念在 Java 中不存在,但与 C 语言有些相似之处。

在 Groovy 中,null、空白、zero 或空值会被评估为 false

 1  def n = null
 2  def b = ""
 3  def z = 0
 4  def el = []
 5  def em = [:]
 6  if (n == null)
 7      println 'n == null'
 8  if (!n)
 9      println '!n'
10  if (b == "")
11      println 'b == ""'
12  if (!b)
13      println '!b'
14  if (z == 0)
15      println 'z == 0'
16  if (!z)
17      println '!z'
18  if (el == [])
19      println 'el == []'
20  if (!el)
21      println '!el'
22  if (em == [:])
23      println 'em == [:]'
24  if (!em)
25      println '!em'

运行它

$ groovy Groovy06b.groovy
n == null
!n
b == “”
!b
z == 0
!z
el == []
!el
em == [:]
!em
$

请注意,Groovy 真值可以出现在 if 语句之外的其他地方。例如

 1  boolean x = ""
 2  println x

现在运行

$groovy Groovy06c.groovy
false
$

需要明确的是,将 “” 赋值给 x 并不是将 “” 转换为 boolean

结论

这里有三个关键要点:第一,==.equals() 的等价性;第二,新的 === 引用相等性检查;第三,Groovy 真值。

作者

如果您喜欢这篇文章,您可能也会喜欢这些