Groovy – The concept of optional typing

We haven’t used any explicit static typing in the way that you’re familiar with in Java. We assigned strings and numbers to variables and didn’t care about the type. Besides this, Groovy implicitly assumes these variables to be of static type java.lang.Object.

Assigning Types:

Groovy offers the choice of assigning types explicitly just as you do in Java. Given table gives examples of optional static type declarations and the dynamic type used at runtime. The def keyword is used to indicate that no particular type is demanded.

Statement

Type of Value

Comment

def a = 1

java.lang.Integer

Implicit typing

def b = 1.0f

java.lang.Float

int c = 1

java.lang.Integer

Explicit typing using the Java primitive

type names

float d = 1

java.lang.Float

Integer e = 1

java.lang.Integer

Explicit typing using reference type names

String f = ‘1’

java.lang.String

It doesn’t matter whether you declare or cast a variable to be of type int or Integer. Groovy uses the reference type (Integer). If you prefer to be concise, and you believe your code’s readers understand Groovy well enough, use int. If you want to be explicit, or you wish to highlight to Groovy newcomers that you really are using objects, use Integer.

It is important to understand that regardless of whether a variable’s type is explicitly declared, the system is type safe. Unlike untyped languages, Groovy doesn’t allow you to treat an object of one type as an instance of a different type without a well-defined conversion being available.

Static versus dynamic typing

The choice between static and dynamic typing is one of the key benefits of Groovy.

Static typing provides more information for optimization, more sanity checks at compile time, and better IDE support; it also reveals additional information about the meaning of variables or method parameters and allows method over-loading. Static typing is also a prerequisite for getting meaningful information from reflection.

Dynamic typing, on the other hand, is not only convenient for the lazy programmer, but also useful for relaying and duck typing. Suppose you get an object as the result of a method call, and you have to relay it as an argument to some other method call without doing anything with that object yourself:

The usage of dynamic typing is calling methods on objects that have no guaranteed type. This is often called duck typing.

For programmers with a strong Java background, it is not uncommon to start programming Groovy almost entirely using static types, and gradually shift into a more dynamic mode over time. This is legitimate because it allows everybody to use what they are confident with.

Overriding operators:

1+1 is just a convenient way of writing 1.plus(1). This is achieved by class

Integer
having an implementation of the plus
method. This convenient feature is also available for other operators.

You can easily use any of these operators with your own classes. Just implement the respective method. Unlike in Java, there is no need to implement a specific interface.

Method-based operators:

Operator

Name

Method

Works with

a + b

Plus

a.plus(b)

Number, string, collection

a-b

Minus

a.minus(b)

Number, string, collection

a*b

Star

a.multiply(b)

Number, string, collection

a/b

Divide

a.div(b)

Number

a%b

Module

a.mod(b)

Integral number

a++

Post increment

a.next()

Number, string, range

++a

Pre increment

a–

Post decrement

a.previous()

Number, string, range

–a

Pre decrement

a**b

Power

a.power(b)

Number

a | b

Numerical or

a.or(b)

Integral number

a & b

Numerical and

a.and(b)

Integral number

a ^ b

Numerical xor

a.xor(b)

Integral number

~ a

Bitwise compliment

a.negate()

Integral number, string (the

latter returning a regular

expression pattern)

a [b]

Subscript

a.getAt(b)

Object, list, map,

String

,

Array

a [b]=c Subscript assignment a.putAt(b, c) Object, list, map,StringBuffer

,

Array

a << b Left shift a.leftShift(b) Integral number, also used like”append” to

StringBuffers,

Writers, Files,

Sockets, Lists

a >>b Right shift a.rightShift(b) Integral number
a >>> b Right shift unsigned a.rightShiftUnsigned(b) Integral number
switch(a){

case b:

}

Classification b.isCase(a) Object, range, list,collection, pattern, closure;

also used with collection c in c.grep(b), which returns all items of c where b.isCase(item)

a==b Equals a.equals(b) Object; considerhashCode()

a

a !=b Not Equal ! a.equals(b) Object
a <=> b Spaceship a.compareTo(b) java.lang.Comparable
a > b Greater than a.compareTo(b) > 0
a >= b Greater than or equal to a.compareTo(b) >= 0
a < b Less than a.compareTo(b) < 0
a <= b Less than or equal to a.compareTo(b) <= 0
a as type Enforced correction a.asType(typeClass) Any type

This is all good in theory, but let’s see how they work in practice.

Overridden operators in action:

We implement equals such that it copes with null comparison. This is Groovy style. The default implementation of the equals
operator doesn’t throw any NullPointerExceptions either. Remember that ==
(or equals) denotes object equality (equal values), not identity
(same object instances).

Operator override:

Bellow code demonstrates an implementation of the equals == and plus
+ operators for a Money class. We allow money of the same form of currency to be added up but do not support multicurrency addition.

We implement equals such that it copes with null comparison. This is Groovy style. The default implementation of the equals operator doesn’t throw any NullPointerExceptions either. Remember that == (or equals) denotes object equality (equal values), not identity (same object instances).

Code:

Overriding equals is straightforward, as we show at override
==
operator. We also provide a hashCode method to make sure equal Money objects have the same hashcode. This is required by Java’s contract for java.lang.Object. The use of this operator is shown at use overhidden==
where one dollar becomes equal to any other dollar.

At Implement + operator, the plus operator is not overridden in the strict sense of the word, because there is no such operator in Money’s superclass (Object). In this case, operator implementing is the best wording. This is used at Use overhidden+,
where we add two Money objects.

To explain the difference between overriding and overloading, here is a possible overload for Money’s plus operator. In case, we would like to add Money as

that overloads the plus method with a second implementation that takes an Integer parameter. The Groovy method dispatch finds the right implementation at runtime.

This example leads to the general issue of how to deal with different parameter types when implementing an operator method.

Making coercion work for you:

Implementing operators is straightforward when both operands are of the same

type. Things get more complex with a mixture of types, say

1 + 1.0

This adds an Integer and a BigDecimal.
One of the two arguments needs to be promoted to the more general type. This is called coercion.

When implementing operators, there are three main issues to consider as part of coercion.

Supported argument types:

You need to decide which argument types and values will be allowed. If an operator must take a potentially inappropriate type, throw an IllegalArgumentException where necessary. For instance, in our Money example, even though it makes sense to use Money as the parameter for the plus operator, we don’t allow different currencies to be added together.

Promoting more specific arguments:

If the argument type is a more specific one than your own type, promote it to your type and return an object of your type. To see what this means, consider how you might implement the plus operator if you were designing the BigDecimal class, and what you’d do for an Integer argument. Integer is more specific than BigDecimal: Every Integer
value can be expressed as a BigDecimal, but the reverse isn’t true. So for the BigDecimal. plus(Integer)operator, we would consider promoting the Integer
to BigDecimal,performing the addition, and then returning another BigDecimal
even if the result could accurately be expressed as an Integer.

Handling more general arguments with double dispatch:

If the argument type is more general, call its operator method with yourself (“this,” the current object) as an argument. This is also called double dispatch,and it helps to avoid duplicated, asymmetric, possibly inconsistent code. Let’s reverse our previous example and consider Integer.plus (BigDecimal operand). We would consider returning the result of the expression operand.plus(this), delegating the work to BigDecimal’s plus(Integer) method. The result would be a BigDecimal, which is reasonable—it would be odd for 1+1.5 to return an Integer but 1.5+1 to return a BigDecimal. Of course, this is only applicable for commutative operators.

Groovy’s conventional behavior:

Groovy’s general strategy of coercion is to return the most general type. Other languages such as Ruby try to be smarter and return the least general type that can be used without losing information from range or precision. The Ruby way saves memory at the expense of processing time. It also requires that the language promote a type to a more general one when the operation would generate an overflow of that type’s range. Otherwise, intermediary results in a complex calculation could truncate the result.

Leave A Comment

Your email address will not be published. Required fields are marked *