Groovy中的类

御弟哥哥 等级 343 0 0

Groovy 中的类

迄今为止,您已经用 Groovy 输出了许多次 “Hello World”,已经操作了集合,用闭包在集合上迭代,也定义了您自己的闭包。做所有这些工作时,甚至还没有讨论那个对 Java 开发人员来说至关重要的概念 — 类。

当然,您已经在这个教程中使用过类了:您编写的最后几个示例就是在不同类的 main() 方法中。而且,您已经知道,在 Groovy 中可以像在 Java 代码中一样定义类。惟一的区别是,不需要使用 public 修改符,而且还可以省略方法参数的类型。这一节将介绍使用 Groovy 类能够进行的其他所有操作。

Song 类

我们先从用 Groovy 定义一个简单的 JavaBean 形式的类开始,这个类称为 Song。

第一步自然是用 Groovy 创建名为 Song 的类。这次还要为它创建一个包结构 — 创建一个包名,例如 org.acme.groovy。

创建这个类之后,删除 Groovy 插件自动生成的 main()。

歌曲有一些属性 — 创作歌曲的艺术家、歌曲名称、风格等等。请将这些属性加入新建的 Song 类,如下所示:

package org.acme.groovy

class Song {
 def name
 def artist
 def genre
}

迄今为止还不错,是不是?对于 Grooovy 的新开发人员来说,还不算太复杂!

Groovy 类就是 Java 类

应该还记得本教程前面说过 Groovy 编译器为用 Groovy 定义的每个类都生成标准的 Java .class。还记得如何用 Groovy 创建 HelloWorld 类、找到 .class 文件并运行它么?也可以用新定义的 Song 类完成同样的操作。如果通过 Groovy 的 groovyc 编译器编译代码(Eclipse Groovy 插件已经这样做了),就会生成一个 Song.class 文件。

这意味着,如果想在另一个 Groovy 类或 Java 类中使用新建的 Song 类,则必须导入 它(当然,除非使用 Song 的代码与 Song 在同一个包内)。

接下来创建一个新类,名为 SongExample,将其放在另一个包结构内,假设是 org.thirdparty.lib。

现在应该看到如下所示的代码:

package org.thirdparty.lib

class SongExample {
 static void main(args) {}
}

类的关系

现在是使用 Song 类的时候了。首先导入实例,并将下面的代码添加到 SongExample 的 main() 方法中。

package org.thirdparty.lib

import org.acme.groovy.Song

class SongExample {
 static void main(args) {
  def sng = new Song(name:"Le Freak", 
    artist:"Chic", genre:"Disco")
 }
}

现在 Song 实例创建完成了!但是仔细看看以前定义的 Song 类的初始化代码,是否注意到什么特殊之处?您应该注意到自动生成了构造函数。

类初始化

Groovy 自动提供一个构造函数,构造函数接受一个名称-值对的映射,这些名称-值对与类的属性相对应。这是 Groovy 的一项开箱即用的功能 — 用于类中定义的任何属性,Groovy 允许将存储了大量值的映射传给构造函数。映射的这种用法很有意义,例如,您不用初始化对象的每个属性。

也可以添加下面这样的代码:

def sng2 = new Song(name:"Kung Fu Fighting", genre:"Disco")

也可以像下面这样直接操纵类的属性:

def sng3 = new Song()
sng3.name = "Funkytown"
sng3.artist = "Lipps Inc."
sng3.setGenre("Disco")

assert sng3.getArtist() == "Lipps Inc."

从这个代码中明显可以看出,Groovy 不仅创建了一个构造函数,允许传入属性及其值的映射,还可以通过 . 语法间接地访问属性。而且,Groovy 还生成了标准的 setter 和 getter 方法。

在进行属性操纵时,非常有 Groovy 特色的是:总是会调用 setter 和 getter 方法 — 即使直接通过 . 语法访问属性也是如此

核心的灵活性

Groovy 是一种本质上就很灵活的语言。例如,看看从前面的代码中将 setGenre() 方法调用的括号删除之后会怎么样,如下所示:

sng3.setGenre "Disco"
assert sng3.genre == "Disco"

在 Groovy 中,对于接受参数的方法,可以省略括号 — 在某些方面,这样做会让代码更容易阅读。

方法覆盖

迄今为止已经成功地创建了 Song 类的一些实例。但是,它们还没有做什么有趣的事情。可以用以下命令输出一个实例:

println sng3

在 Java 中这样只会输出所有对象的默认 toString() 实现,也就是类名和它的 hashcode(即 org.acme.groovy.Song@44f787)。下面来看看如何覆盖默认的 toString() 实现,让输出效果更好。

在 Song 类中,添加以下代码:

String toString(){
 "${name}, ${artist}, ${genre}"
}

根据本教程已经学到的内容,可以省略 toString() 方法上的 public 修改符。仍然需要指定返回类型(String),以便实际地覆盖正确的方法。方法体的定义很简洁 — 但 return 语句在哪?

不需要 return

您可能已经想到:在 Groovy 中可以省略 return 语句。Groovy 默认返回方法的最后一行。所以在这个示例中,返回包含类属性的 String。

重新运行 SongExample 类,应该会看到更有趣的内容。toString() 方法返回一个描述,而不是 hashcode。

特殊访问

Groovy 的自动生成功能对于一些功能来说很方便,但有些时候需要覆盖默认的行为。例如,假设需要覆盖 Song 类中 getGenre() 方法,让返回的 String 全部为大写形式。

提供这个新行为很容易,只要定义 getGenre() 方法即可。可以让方法的声明返回 String,也可以完全省略它(如果愿意)。下面的操作可能是最简单的:

def getGenre(){
 genre.toUpperCase()
}

同以前一样,这个简单方法省略了返回类型和 return 语句。现在再次运行 SongExample 类。应该会看到一些意外的事情 —— 出现了空指针异常。

空指针安全性

如果您一直在跟随本教程,那么应该已经在 SongExample 类中加入了下面的代码:

assert sng3.genre == "Disco"

结果在重新运行 SongExample 时出现了断言错误 — 这正是为什么在 Eclipse 控制台上输出了丑陋的红色文字。(很抱歉使用了这么一个糟糕的技巧)

幸运的是,可以轻松地修复这个错误:只要在 SongExample 类中添加以下代码:

println sng2.artist.toUpperCase()

但是现在控制台上出现了更多的 红色文本 — 出什么事了?!

可恶的 null

如果回忆一下,就会想起 sng2 实例没有定义 artist 值。所以,在调用 toUpperCase() 方法时就会生成 Nullpointer 异常。

幸运的是, Groovy 通过 ? 操作符提供了一个安全网 — 在方法调用前面添加一个 ? 就相当于在调用前面放了一个条件,可以防止在 null 对象上调用方法。

例如,将 sng2.artist.toUpperCase() 行替换成 sng2.artist?.toUpperCase()。请注意,也可以省略后面的括号。(Groovy 实际上也允许在不带参数的方法上省略括号。不过,如果 Groovy 认为您要访问类的属性而不是方法,那么这样做可能会造成问题。)

重新运行 SongExample 类,您会发现 ? 操作符很有用。在这个示例中,没有出现可恶的异常。现在将下面的代码放在这个类内,再次运行代码。

def sng4 = new Song(name:"Thriller", artist:"Michael Jackson")
println sng4

就是 Java

您将会注意到,虽然预期可能有异常,但是没有生成异常。即使没有定义 genre,getGenre() 方法也会调用 toUpperCase()。

您还记得 Groovy 就是 Java,对吧?所以在 Song 的 toString() 中,引用了 genre 属性本身,所以不会调用 getGenre()。现在更改 toString() 方法以使用 getGenre(),然后再看看程序运行的结果。

String toString(){
 "${name}, ${artist}, ${getGenre()}"
}

重新运行 SongExample,出现类似的异常。现在,请自己尝试修复这个问题,看看会发生什么。

另一个方便的小操作符

希望您做的修改与我的类似。在下面将会看到,我进一步扩充了 Song 类的 getGenre() 方法,以利用 Groovy 中方便的 ? 操作符。

def getGenre(){
 genre?.toUpperCase()
}

? 操作符时刻都非常有用,可以极大地减少条件语句。

对 Groovy 进行单元测试

本教程一直都强调 Groovy 只是 Java 的一个变体。您已经看到可以用 Groovy 编写并使用标准的 Java 程序。为了最后一次证明这点,在结束本教程之前,我们将通过 JUnit 利用 Java 对 Song 类进行单元测试。

将 JUnit 加入 Eclipse 项目

为了跟上本节的示例,需要将 JUnit 加入到 Eclipse 项目中。首先,右键单击项目,选择 Build Path,然后选择 Add Libraries,如图 14 所示:

图 14. 将 JUnit 加入到项目的构建路径

Groovy中的类

会出现 Add Library 对话框,如图 15 所示。

图 15. 从库列表中选择 JUnit Groovy中的类

选择 JUnit 并单击 Next 按钮。应该会看到如图 16 所示的对话框。选择 JUnit3 或 4— 具体选择哪项全凭自己决定 — 并单击 Finish 按钮。

图 16. 选择 JUnit 3 或 JUnit 4

Groovy中的类

设置新的测试用例

现在在项目的类路径中加入了 JUnit,所以能够编写 JUnit 测试了。请右键单击 java 源文件夹,选择 New,然后选择 JUnit Test Case。定义一个包,给测试用例命名(例如 SongTest),在 Class Under Test 部分,单击 Browse 按钮。

请注意,可以选择用 Groovy 定义的 Song 类。图 17 演示了这一步骤:

图 17.找到 Song 类 Groovy中的类

选择该类并单击 OK(应该会看到与图 18 类似的对话框)并在 New JUnit Test Case 对话框中单击 Finish 按钮。

图 18. Song 的新测试用例 Groovy中的类

定义测试方法

我选择使用 JUnit 4;所以我定义了一个名为 testToString() 的测试方法,如下所示:

package org.acme.groovy;

import org.junit.Test;

public class SongTest {

 @Test
 public void testToString(){}

}

测试 toString

显然,需要验证 toString() 方法是否没有问题,那么第一步该做什么呢?如果想的是 “导入 Song 类”,那么想得就太难了 —Song 类在同一个包内,所以第一步是创建它的实例。

在创建用于测试的 Song 实例时,请注意不能通过传给构造函数的映射完全初始化 — 而且,如果想自动完成实例的 setter 方法,可以看到每个 setter 接受的是 Object 而不是 String(如图 19 所示)。为什么会这样呢?

图 19. 所有的 setter 和 getter

Groovy中的类

Groovy 的功劳

如果回忆一下,就会记得我在本教程开始的时候说过:

因为 Java 中的每个对象都扩展自 java.lang.Object,所以即使在最坏情况下,Groovy 不能确定变量的类型,Groovy 也能将变量的类型设为 Object然后问题就会迎刃而解。 现在回想一下,在定义 Song 类时,省略了每个属性的类型。Groovy 将自然地将每个属性的类型设为 Object。所以,在标准 Java 代码中使用 Song 类时,看到的 getter 和 setter 的参数类型和返回类型全都是 Object。

修正返回类型

为了增添乐趣,请打开 Groovy Song 类,将 artist 属性改为 String 类型,而不是无类型,如下所示:

package org.acme.groovy

class Song {
 def name
 String artist
 def genre

 String toString(){
  "${name}, ${artist}, ${getGenre()}"
 }

 def getGenre(){
  genre?.toUpperCase()
 }
}

现在,回到 JUnit 测试,在 Song 实例上使用自动完成功能 — 看到了什么?

在图 20 中(以及您自己的代码中,如果一直跟随本教程的话),setArtist() 方法接受一个 String,而不是Object。Groovy 再次证明了它就是 Java,而且应用了相同的规则。

图 20. String,而不是 object

Groovy中的类

始终是普通的 Java

返回来编写测试,另外请注意,默认情况下 Groovy 编译的类属性是私有的,所以不能直接在 Java 中访问它们,必须像下面这样使用 setter:

@Test
public void testToString(){
 Song sng = new Song();
 sng.setArtist("Village People");
 sng.setName("Y.M.C.A");
 sng.setGenre("Disco");

 Assert.assertEquals("Y.M.C.A, Village People, DISCO", 
   sng.toString());
}

编写这个测试用例余下的代码就是小菜一碟了。测试用例很好地演示了这样一点:用 Groovy 所做的一切都可以轻易地在 Java 程序中重用,反之亦然。用 Java 语言执行的一切操作和编写的一切代码,在 Groovy 中也都可以使用。

结束语

如果说您从本教程获得了一个收获的话(除了初次体验 Groovy 编程之外),那么这个收获应该是深入地认识到 Groovy 就是 Java,只是缺少了您过去使用的许多语法规则。Groovy 是没有类型、没有修改符、没有 return、没有 Iterator、不需要导入集合的 Java。简而言之,Groovy 就是丢掉了许多包袱的 Java,这些包袱可能会压垮 Java 项目。

但是在幕后,Groovy 就是 Java。

我希望通向精通 Groovy 的这第一段旅程给您带来了快乐。您学习了 Groovy 语法,创建了几个能够体验到 Groovy 的生产力增强功能的类,看到了用 Java 测试 Groovy 类有多容易。还遇到了第一次使用 Groovy 的开发者常见的一些问题,看到了如何在不引起太多麻烦的情况下解决它们。

尽管您可能觉得自己目前对 Groovy 还不是很熟练,但您已经走出了第一步。您可以用目前学到的知识编写自己的第一个 Groovy 程序 — 毕竟,您已经设置好了同时支持 Groovy 和 Java 编程的双重环境!作为有趣的练习,您可以试试用 Gant 设置下一个的自动构建版本,Gant 是基于 Ant 的构建工具,使用 Groovy 来定义构建,而不是使用 XML。当您对 Groovy 更加适应时,可以试着用 Groovy on Grails 构建 Web 应用程序模块 — 顺便说一下,这是下一篇教程的主题。

收藏
评论区

相关推荐

Gradle技术之一 Groovy语法精讲
Gradle技术之一 Groovy语法精讲 gradle脚本是基于groovy语言开发的,想要学好gradle必须先要对groovy有一个基本的认识 1. Groovy特点 groovy是一种DSL语言,所谓的DSL语言,就是专门针对某一特定领域的语言,专精而不专广 是一种基于JVM的开发语言,也是编译成class字节码文件 结合和Pytho
Gradle技术之二 Groovy对文件的操作
Groovy对文件的操作 对文件的遍历 假设文件的原始内容为: hello,world 这里是北京 andorid and ios are good system 第一种方法:使用 eachLine() //1.1 new 一个File def file new File(filepath) //1.2 groovy对文件的遍历 file.
Gradle系列之三 Gradle概述以及生命周期
1 Gradle是一种编程框架 gradle主要由以下三部分组成 1 groovy核心语法 2 build script block 3 gradle api 注:本章所有的代码都在 https://github.com/jiulu313/gradledemo.git 如下图 73485499237410.png(https://img
JavaScript中的类型
JavaScript中的类型 一、关于类型 什么叫做类型?简单地说,类型就是把内存中的一个二进制序列赋予某种意义。比如,二进制序列0100 0000 0111 0000 0001 0101 0100 1011 1100 0110 1010 0111 1110 1111 1001 1110如果看作是64位无符号整数类型就是4
Groovy初探
开始之前 了解本教程的主要内容,以及如何从中获得最大收获。 关于本教程 如果现在有人要开始完全重写 Java,那么 Groovy 就像是 Java 2.0。Groovy 并没有取代 Java,而是作为 Java 的补充,它提供了更简单、更灵活的语法,可以在运行时动态地进行类型检查。您可以使用 Groovy 随意编写 Java 应用程序,连接 Java
Groovy基础与循环语句
Groovy 入门 在这一节中,将真正开始进行 Groovy 编程。首先,学习如何轻松地安装 Groovy(通过 Eclipse Groovy 插件),然后从一些有助于了解 Groovy 的简单示例开始。 轻松安装 Groovy 为了迅速开始使用 Groovy,需要做的全部工作就是安装 Eclipse 的 Groovy 插件。打开 Ecliplse,在
Groovy 集合与闭包
Groovy 集合 在 Groovy 提供的所有方便的快捷方式和功能中,最有帮助的一个可能就是内置的 集合。回想一下在 Java 编程中是如何使用集合的 — 导入 java.util 类,初始化集合,将项加入集合。这三个步骤都会增加不少代码。 而 Groovy 可以直接在语言内使用集合。在 Groovy 中,不需要导入专门的类,也不需要初始化对象。集合是语
Groovy中的类
Groovy 中的类 迄今为止,您已经用 Groovy 输出了许多次 “Hello World”,已经操作了集合,用闭包在集合上迭代,也定义了您自己的闭包。做所有这些工作时,甚至还没有讨论那个对 Java 开发人员来说至关重要的概念 — 类。 当然,您已经在这个教程中使用过类了:您编写的最后几个示例就是在不同类的 main() 方法中。而且,您已经知道,在
使用jsp直接执行定时任务
使用jsp直接执行定时任务servicehtml<%@ page import"com.leasing.emogo.framework.util.ApplicationContextUtils" %<%@ page import"job.dsc.GetInfoByAssetPackageJob" %<%@ page contentType
java中List数组遍历删除
List数组遍历删除 环境 jdk8 junit 单元测试 正解java// 正解1, jdk自带的addAll方法 @Test public void test18() { String strs {"12","34","56","78","90"}; List<String list Ar
idea运行junit测试程序报错command line is too long. shorten command line for
idea运行junit测试程序报错command line is too long. shorten command line for ... 解决方法在项目根目录.idea/workspace.xml文件中添加一行代码xml<component name"PropertiesComponent"  ... <property name"d
Java 实用类
实用类 枚举 Math Random String StringBuffer 日期类 枚举枚举(Enum)是一种有确定取值区间的数据类型,它本质上是一种类,具有简洁、安全、方便等特点。可以这样理解,枚举的值被约束到一个特定的范围,只能取这个范围以内的值。我们为什么要用枚举呢?我们在描述对象的一些属性特征时,可选择的值是一个特定范围的,不能随便定义。比如性别只
盘点golang中的开发神器
本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star。在Java中,我们用Junit做单元测试,用JMH做性能基准测试(benchmark),用asyncprofiler剖析cpu性能,用jstack、jmap、arthas等来排查问题。作为一名比较新的编程语言,golang的这些工具是否更加好用呢? 单元测
2021年度最全面JVM虚拟机,类加载过程与类加载器
前言类装载器子系统是JVM中非常重要的部分,是学习JVM绕不开的一关。一般来说,Java 类的虚拟机使用 Java 方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表
2021年度最全面JVM虚拟机,类加载过程与类加载器
前言类装载器子系统是JVM中非常重要的部分,是学习JVM绕不开的一关。一般来说,Java 类的虚拟机使用 Java 方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表