Groovy中的显式类型声明:有时候还是从不?

21
稍后:仍然无法确定Groovy是否具有静态类型(似乎不是),或者使用显式类型生成的字节码是否不同(似乎是)。 无论如何,进入问题:
Groovy和其他动态语言(至少Ruby)之间的主要区别之一是,您可以在需要时显式地为变量指定类型。
话虽如此,在Groovy中何时应该使用静态类型?以下是我能想到的一些可能的答案:
1.仅当存在性能问题时。 在Groovy中,静态类型变量更快。(或者它们是吗?这个链接有一些疑问)
2.对于类的公共接口(方法,字段),以便您获得自动完成。这是可能/正确/完全错误的吗?
3.从不,它只会使代码混乱,并破坏使用Groovy的目的。
4.当您的类将被继承或使用时
我不仅对您个人的做法感兴趣,更重要的是对您在使用Groovy编写的项目中看到的情况。 什么是常规做法?
注意:如果此问题在某种程度上是错误的或缺少某些静态-动态类别,请告诉我,我会进行修正。

我认为标题应该是“Groovy中的显式类型”,因为这种语言没有静态类型。 - Pablo Fernandez
@Pablo Fernandez,稍微修改了一下问题。 - Dan Rosenstark
4个回答

19

根据我的经验,这并没有一个固定的规范。有些人频繁使用类型,而有些人则从不使用。就我个人而言,在我的方法签名中(对于参数和返回值)我总是尽量使用类型。例如,我总是像这样编写方法:

Boolean doLogin(User user) {
// implementation omitted
}

虽然我可以这样写

def doLogin(user) {
// implementation omitted
}

我这样做是出于以下原因:

  1. 文档:其他开发人员(包括我自己)不需要阅读实现细节就能知道该方法提供和返回哪些类型。
  2. 类型安全:虽然 Groovy 没有编译时检查,但如果我使用非 User 参数调用静态类型版本的 doLogin,它将立即失败,因此问题很容易修复。如果我调用动态类型版本,则该方法被调用后会在“一段时间”后失败,而故障的原因可能并不明显。
  3. 代码补全:当使用良好的 IDE(如 IntelliJ)时,这特别有用,因为它甚至可以为域类中动态添加的方法提供完成。

我在方法的实现中也经常使用类型,理由同上。事实上,我只有在以下情况下才不使用类型:

  1. 我确实想支持各种类型。例如,一个将字符串转换为数字的方法也可以将字符串集合或数组转换为数字。
  2. 懒得!如果变量的作用域非常短,我已经知道要调用哪些方法,而且我还没有导入所需的类,那么声明类型似乎比它值得更多的麻烦。

顺便说一下,我不会对你链接的那篇博客文章过于信任,它声称静态类型 Groovy 比非静态类型 Groovy 快得多。我以前从未听说过这个问题,并且我也没有发现太有说服力的证据。


很棒的答案,谢谢Don。我不知道那篇博客文章,但如果我有时间做一些基准测试,我会在这里回复。对我来说似乎是合理的,我认为字节码取决于如何规定变量。 - Dan Rosenstark
1
一年半后,我正在使用Objective-C进行工作,它基本上与Groovy相同(动态分派,仅在编译时有类型),而大多数人尽可能地使用类型,这符合您的工作方式并且通常是一个好主意。 - Dan Rosenstark

2

我曾经参与过几个Groovy项目,我们遵循以下约定:

  • 公共方法中必须指定所有类型。

    public int getAgeOfUser(String userName){ ... }

  • 所有私有变量都使用def关键字声明。

这些约定使您能够实现许多事情。

首先,如果使用联合编译,您的Java代码将能够轻松地与Groovy代码交互。其次,这种明确的声明使得大型项目中的代码更易读和可持续。当然,自动完成也是一个重要的好处。

另一方面,方法的范围通常很小,您不需要明确声明类型。顺便说一句,现代IDE即使您使用defs也可以自动完成您的局部变量。


非常有趣。使用def来定义私有变量与仅声明类型相比的优势是什么?你已经知道它,而Groovy不会强制转换。 - Dan Rosenstark
1
如果您有一个本地作用域,您就知道变量的类型,看起来更好。例如:`def factory = PersonContactFactory.newInstance()``PersonContactFactory contact = PersonContactFactory.newInstance()`清晰一些。这是我喜欢在本地作用域中使用“defs”的唯一原因。无论如何,您的IDE都知道变量的类型,您没有任何静态检查。 - Victor Savkin

1

我曾经看到类型信息主要用于公共方法的服务类中。根据参数列表的复杂程度,即使在这里,我通常也只看到返回类型被输入。例如:

class WorkflowService {
    ....
    WorkItem getWorkItem(processNbr) throws WorkflowException {
        ...
        ...
    }
}

我认为这很有用,因为它明确告诉服务的用户他们将要处理的类型,并且有助于IDE中的代码辅助。

谢谢,这正是我的想法。 - Dan Rosenstark

0

Groovy不支持静态类型。看看自己:

public Foo func(Bar bar) {
  return bar
}
println("no static typing")

保存并编译该文件,然后运行它。


文件可以运行,但如果您尝试将非“Bar”传递给函数,则会出现“groovy.lang.MissingMethodException: No signature of method:”错误。 - Dan Rosenstark
我相信是在运行时。 - Pablo Fernandez
不确定,Pablo:我的理解是,如果您使用类型或者不使用类型,Groovy 生成的字节码是不同的。 - Dan Rosenstark
1
是的,它会静态地为方法参数分配类型。 但直到运行时才检查方法调用的类型。 - slim
2
@slim,我认为你错了,因为Groovy 2已经支持。使用groovy v. 2.3.8@TypeChecked class Foo { <your 'func' method> },你的代码无法通过groovyc编译:[Static type checking] - Cannot return value of type Bar on method returning type Foo - Kevin Meredith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接