Groovy中的类

御弟哥哥 等级 667 0 0
标签: groovyjunitJava

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
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() 方法中。而且,您已经知道,在
java动态调用groovy代码
pom文件引入groovy包 <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>2.5.5</version> </depende
java程序员为什么使用Groovy
     一直听说java世界里有个Groovy,但是一直没时间去了解它究竟是一个怎么样子的。我们现在的项目里放了很多开源包,而且项目中做流程的时候,规则就是用Groovy实现的。近来闲来无事,于是开始认真的看看Groory究竟有什么好的。其实最初我接触它是因为Grails这一个框架,这两个都是为了实现一个目的,java的敏捷开发,与java的无缝对接。在某
Using Spock to test Java or Groovy applications
Before ====== Spock是用于groovy项目的单元测试框架,这个框架简单易用,值得推广。 Coding ====== <dependencies> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifact
GROOVY预览
**1.Groovy 中的闭包** Java的一些不足可以通过使用groovy的闭包很好的解决,通过下面这个例子来看看使用闭包的优势: 在Java中遍历一个集合的方法是使用迭代,就像下面这样: def acoll = ["Groovy", "Java", "Ruby"] for(Iterator iter = acoll.iterator()
Gradle的安装与配置及Spring源码导入(亲测)
### 前言 我这边使用的gradle版本是:gradle-6.5.1 idea版本是:ideaIU-2019.3.1 jdk:1.8 ### Gradle简介 Gradle是源于Apache Ant和Apache Maven概念的项目自动化构建开源工具,它使用一种基于Groovy的的特定领域语言(
Groovy Android build.gradle文件
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.2.3' classpath '
Groovy初体验:构建高性能JVM应用
![VAIO王者归来](http://upload-images.jianshu.io/upload_images/9824247-2c94b4372011cfce.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) * * * * * * 为什么要学Groovy ----------- G
Groovy常用编程知识点简明教程
###概述### Groovy 是一门基于JVM的动态类型语言,可以与 Java 平台几乎无缝融合(与Java代码一样编译成字节码)。 使用 Groovy ,可以增强 Java 应用的灵活性和可扩展性,提升开发效率。使用 Groovy + Spock 组合也更容易编写简洁易懂的单测。熟练使用 Groovy 编程是很有益处的。 本文针对已有一定java开发
Micro
micro-mvc框架,借助nhmicro框架和groovy技术使所有的controller、servicebean、dao和sql脚本都支持动态热部署和调试。 **Controller层** Controller层groovy中使用注解MicroUrlMapping设置url地址 类上的注解必须有并作为根路径 各个方法中设置子路径 比如下面的gro
springcloud按照jar运行时支持脚本热部署
springcloud按照可运行jar包部署时,如果直接将groovy打入jar则不支持热部署。 需要将groovy放置到另一个git目录下编写,开发时使用linked目标放置到project中,部署是不打入jar中。 借助micro-git-sync模块功能在启动时从git仓库中下载groovy脚本加载到springcloud应用中, 同时还可以支持动