Groovy 确保正确的工作

当两个数的类型相同时实现操作符是非常简单的,不同类型之间进行操作要复杂一些,如:1+1.0这是一个Integer和BigDecimal的加法,将返回什么类型呢?

但这是个一般问题,两个参数中的一个需要升到更通用的类型,这叫做类型强制转换(coercion)。

在实现操作符的时候,类型强制转换有三个主要的问题需要考虑。

支持的参数类型

实现操作符的时候需要考虑哪种参数类型和值是被允许的,如果一个操作符接收了一个不适当的参数类型,那么在必要的时候要抛出IllegalArgumentException。

例如,在我们的Money的例子中,尽管能使用Money可以作为plus操作符的参数,但这里不运行不同的货币进行相加。

设置更明确的参数

如果参数类型比定义的更明确(译者注:定义的参数类型是父类(或者接口),传递给操作符的参数是子类(或者实现类)),那么groovy将使用和返回定义的类型,为了明白这个意思,思考一下如何为BigDecimal类设计plus操作符,该操作符接受一个Integer 类型的参数。

Integer比BigDecimal更明确,每一个Integer都能表示为一个BigDecimal,但反过来是不行的,所以对于BigDecimal.plus(Integer)操作符来说,我们考虑把Integer提升到BigDecimal来执行加操作,然后返回另外一个BigDecimal——即使是一个整数结果。

使用双派发处理更多的通用参数

如果参数的类型更加通用,使用当前对象作为参数调用参数自身的操作符方法(用this 表示当前对象),将操作符方法派给参数的操作符方法,这也叫做双派发,并且这能避免代 码重复,和可能不一致的代码,反过来考虑一下前面的例子Integer.plus(BigDecimal operand)。

考虑表达式operand.plus(this)表达式的结果,把工作代理给BigDecimal的plus(Integer)方法,结果是一个BigDecimal,这是合理的,1 + 1.5返回一个Integer,但是1.5 + 1返回BigDecimal。

当然,这只适用于可交换的操作符,必须严格测试,避免死循环。