We can restrict the types of values a variable may hold by specifying some restricting class instead of 'def':

import org.codehaus.groovy.runtime.typehandling.GroovyCastException

def v= 3 //variable v can hold any value
v= 'helicopter'
v= false
v= new StringBuffer()
v= null

int i= 15 //variable i can only hold integer values
i= 'A'
assert i == 65 //'A' casted to its integer value

//unable to cast boolean value to integer
try{ i= false; assert 0 }catch(e){ assert e in GroovyCastException }

Closure c= {it * 3} //variable c can only hold Closures
try{ c= false; assert 0 }catch(e){ assert e in GroovyCastException }
      //unable to cast boolean value to Closure

StringBuffer s= new StringBuffer('morning')
      //variable s can only hold StringBuffers
try{ s= { it * 5 }; assert 0 }catch(e){ assert e in GroovyCastException }
      //unable to cast Closure value to StringBuffer

When we assign values not of a variable's type to the variable, sometimes it may be 'cast' to the type, other times an exception is thrown:

import org.codehaus.groovy.runtime.typehandling.GroovyCastException

int i
i= 45L; assert i == 45i
i= 45.1f; assert i == 45i
try{ i= '42'; assert 0 }catch(e){assert e in GroovyCastException}
try{ i= false; assert 0 }catch(e){assert e in GroovyCastException}

//long similar to int

byte by
by= 200i; assert by == -56

//short similar to byte

float f
f= 123i; assert f == 123.0f
try{ f= '42.1'; assert 0 }catch(e){assert e in GroovyCastException}

//double similar to float

BigInteger bi
bi= 42L; assert bi == 42g
try{ bi= '421'; assert 0 }catch(e){assert e in GroovyCastException}

BigDecimal bd
bd= 42.1f; assert bd == 42.1g
try{ bd= '4.21'; assert 0 }catch(e){assert e in GroovyCastException}

boolean b
b= 0; assert ! b
b= 1i; assert b
b= 1g; assert b
b= 1.1g; assert b
b= 1.1f; assert b
b= ''; assert ! b
b= 'a'; assert b
b= 'abc'; assert b
b= null; assert ! b

char c
c= 'a'; assert c == ('a' as char)
try{ c= 'abc'; assert 0 }catch(e){assert e in GroovyCastException}

String s
s= 42i; assert s == '42'
s= 42L; assert s == '42'
s= 42g; assert s == '42'
s= 42.1g; assert s == '42.1'
s= 42.100g; assert s == '42.100'
s= 42.1f; assert s == '42.1'

StringBuffer sb
try{ sb= 'abc'; assert 0 }catch(e){ assert e in GroovyCastException }

We can statically type Closure parameters. The casting is more restrictive than for assigning to variables:

import org.codehaus.groovy.runtime.typehandling.GroovyCastException

int i
def toTriple= {int n -> n * 3}
i= 5
assert toTriple(5) == 15

//a float is cast to an integer when assigning to a variable, but not when
//passing as a parameter...
i= 5.0f
try{ toTriple(5.0f); assert 0 }
catch(e){assert e.class in MissingMethodException}

//a String can't cast to an integer, either when assigning to a variable or
//passing as a parameter...
try{ i= 'abc'; assert 0 }
catch(e){assert e.class in GroovyCastException}
try{ toTriple('abc'); assert 0 }
catch(e){assert e.class in MissingMethodException}

We can also statically type the variable-numbered parameters in a closure:

def c = { int[] args ->
  args.toList().inject(0){ flo, it-> flo + it }
}
assert c( 5 ) == 5
assert c( 4, 2, 3 ) == 9
try{ c( 2, 'abc' ); assert 0 }catch(e){ assert e in MissingMethodException }

We can statically type function parameters:

def f(String s, int i){ ([s]*i).join(',') }
assert f('abc', 3) == 'abc,abc,abc'

def f(int n, int i){ "$n * $i" } //another function f defined with same
                                 //number of but different types of parameters
assert f(4, 5) == '4 * 5'
assert f('a', 5) == 'a,a,a,a,a'
    //correct function selected based on parameter types...
try{ f(4, 'x'); assert 0 }catch(e){ assert e in MissingMethodException }
    //...or no method selected

We can statically type the return type from a function. Casting a returned value of a different type follows the same rules as for assigning to variables:

String f(){ 'abc' }
assert f() == 'abc'

int g(){ 2.4f }
assert g() == 2i

We can statically type method parameters just like we do with function parameters, including selecting a method based on its parameter types, for both static methods and instance methods:

//static methods...
class A{
  static f(String s, int i){ ([s]*i).join(',') }
  static f(int n, int i){ "$n * $i" } //another method f defined with same
                                  //number of but different types of parameters
}
assert A.f('abc', 3) == 'abc,abc,abc'
assert A.f(4, 5) == '4 * 5'
assert A.f('a', 5) == 'a,a,a,a,a'
    //correct method selected based on parameter types...
try{ A.f(4, 'x'); assert 0 }catch(e){ assert e in MissingMethodException }
    //...or no method selected

//instance methods...
class Counter{
  def count = 0
  def incr( String n ){ count += new Integer(n) }
  def incr( int n ){ count += n }
}
def c= new Counter(count: 5)
c.incr(3)
c.incr('4')
try{ c.incr(2.5); assert 0 }catch(e){ assert e in MissingMethodException }
assert c.count == 12

We can statically type the return type from a method, just as we can from a function, both static and instance methods:

class A{
  static String f(){ 'abc' }
  static int g(){ 2.4f }
  byte h(){ 200i }
}
assert A.f() == 'abc'
assert A.g() == 2i
assert new A().h() == -56

Property getters and setters can accept and return any statically-typed value:

class Counter{
  def count= 0
  void setCount(int n){ count= n*2 } //set the value to twice what's supplied
  String getCount(){ 'count: '+ count }
      //return the value as a String with 'count: ' prepended
}
def c= new Counter()
c.count= 23
assert c.count == 'count: 46'

A list can be cast to a class using that class's constructor:

class A{
  int x,y
  A(x,y){ this.x=x; this.y=y } //2-arg constructor
  String toString(){ "x: $x; y: $y" }
}
A a
a= [1,2] //2-element list causes 2-arg constructor of A to be called
assert  a.class == A && a.toString() == 'x: 1; y: 2'

Statically-Typed Arrays

We can statically type an Object array variable:

Object[] oa= new Object[2]
assert oa.class in Object[] && oa.size() == 2 && oa[0,1] == [null, null]

oa= 7 //if we assign another scalar value, it's wrapped into an array
assert oa.class in Object[] && oa.size() == 1 && oa[0] == 7

oa= [3, 5] //if we assign another collection value, it's cast to an array
assert oa.class in Object[] && oa.size() == 2 && oa[0,1] == [3, 5]

def map= ['a':4, 'b':8, 'c':12]
oa= map
assert oa.class in Object[] && oa.size() == 3
  //it's cast to an array of MapEntrys
oa.each{ assert it.key in map.keySet() && it.value == map[it.key] }

We can statically type a variable not only as an array, but as a certain type of array:

int[] ia
ia= 7.5
assert ia.class in int[] && ia.size() == 1 && ia[0] == 7i
    //assigned value above cast to an integer array

try{ ia= ['abc', 'def']; assert 0 }catch(e){ assert e in ClassCastException }
    //can't cast Strings to Integers

We can instead statically type each array element:

def a= new int[3]
assert a[0] == 0 && a[1] == 0 && a[2] == 0 //default value is 0

a[0]= 7.5
assert a[0] == 7i //assigned value in above line was cast to an integer

try{ a[1]= 'abc'; assert 0 }catch(e){ assert e in ClassCastException }
                                          //can't cast String to an Integer

Statically typing both the variable and each element allows both array assignments and element assignments to be cast or disallowed:

int[] ia= new int[3]
ia[0]= 7.5
assert ia[0] == 7i
ia= 7.5
assert ia.class in int[] && ia.size() == 1 && ia[0] == 7i

A multidimensional array type casts its assigned value in various ways:

//a scalar value is cascadingly wrapped by arrays...
Object[][] ia
ia= 7.5
assert ia in Object[][] && ia.size() == 1 &&
       ia[0] in Object[] && ia[0].size() == 1 &&
       ia[0][0] == 7.5

//a one-dimensional vector value is array-wrapped at the innermost level...
ia= ['a', 'b', 'c']
assert ia in Object[][] && ia.size() == 3 &&
       ia[0] in Object[] && ia[0].size() == 1 &&
       ia[0][0] == 'a' && ia[1][0] == 'b' && ia[2][0] == 'c'

Interfaces

Groovy enables a construct known as an interface, which classes can implement. We can test for implemented interfaces with the 'in' operator:

class A{} //a standard class definition, though without any fields,
          //properties, or methods
def a= new A()
assert a in A

interface X{}
class B implements X{} //a class can implement an interface
def b= new B()
assert b in B && b in X
    //'in' tests for the class and for interfaces implemented
assert ! (a in X)

interface Y{}
interface Z{}
class C implements X, Y, Z{} //a class can implement more than one interface
def c= new C()
assert c in C && c in X && c in Y && c in Z

Interfaces can contain method declarations. Each declared method must be defined in implementing classes:

interface X{
  String sayPies(int i)
}

class A implements X{
  String sayPies(int n){ "There are $n pies!" } //sayPies(int) in X defined
  String sayBirds(int n){ "There are $n birds!" }
}
def a= new A()
assert a.sayPies(24) == 'There are 24 pies!'

//class B implements X{}
    //a compile error when uncommented: sayPies(int) must be implemented

//these each give a compile error when uncommented...
//class C implements X{ String sayPies(float n){ "$n" } } //wrong parameter type
//class D implements X{ Object sayPies(int n){ "$n" } } //wrong return type

An interface can also be composed of other interfaces, using the 'extends' keyword:

interface X{
  def x1(int i)
  def x2()
}
interface Y{
  def x1(int i)
  def y()
}
interface Z extends X, Y{ }
    //it's OK if a method, here x1(int), is in more than one interface

class A implements Z{
  def x1(int i){ i }
  def x2(){ 2 }
  def y(){ 3 }
}
assert new A().x1( 1 ) == 1

We can implement an interface with map syntax:

interface X{
  int echo(int i)
  def sayTarts(int i)
  String sayPies(int i)
}
def a= [
  echo: {n-> n},
  sayTarts: {n-> "There are $n tarts!"},
  sayPies: {n-> "There are $n pies!" as String},
    //explicit cast from GString to String required here
] as X

assert a.echo(12) == 12
assert a.sayTarts(18) == 'There are 18 tarts!'
assert a.sayPies(24) == 'There are 24 pies!'

//when interface has only one method, we don't need a map, but can assign and
//cast the closure directly...
interface Y{
  def sayCakes(int i)
}
def b= {n-> "There are $n cakes!"} as Y

assert b.sayCakes(36) == 'There are 36 cakes!'

Interfaces can also have fields, but their values can't be changed:

interface X{
  int status= 1 //constant field on interface
  int changeCounter()
}
class A implements X{
  int counter= 1 //updateable field on class itself
  int changeCounter(){ counter++ }
  int changeStatus(){ status++ }
}
def a= new A()
a.changeCounter() //field 'counter' can be changed...
try{ a.changeStatus(); assert 0 }catch(e){ assert e in IllegalAccessException }
        //...but field 'status' can't

Static Typing with Interfaces

We can use an interface, instead of a class, to statically type a variable, field, parameter, etc:

import org.codehaus.groovy.runtime.typehandling.GroovyCastException

interface X{}
class A implements X{}
class B{}
X a
a= new A()
try{ a= new B(); assert 0 }catch(e){ assert e in GroovyCastException }

Groovy supplies many interfaces we can use to statically type variables. Some have no methods, eg, Serializable, while others have one or more:

class A implements Serializable{}
    //Serializable interface marks class A via the 'in' operator
assert A in Serializable

//class B implements Closeable{}
    //compile error when uncommented: method close() must be defined

class C implements Closeable{
  void close(){}
    //Closeable interface signifies that this close() method is present
}
def c= new C()
if( c in Closeable ) c.close()

We've met the Comparator interface in the tutorial on Collections, and the Iterator interface in the tutorial on Control Structures.

Many Groovy classes we've met implement interfaces, which we can use to statically type variables:

import org.codehaus.groovy.runtime.typehandling.GroovyCastException

List list1= new ArrayList(),
     list2= [],
     list3= new LinkedList()
assert list1 in ArrayList &&
       list2 in ArrayList &&
       list3 in LinkedList

Set set1= new HashSet(),
    set2= list1,
    set3= list3,
    set4= new TreeSet()
assert [set1, set2, set3].every{ it in HashSet } &&
       set4 in TreeSet

SortedSet ss1= new TreeSet(),
          ss2
try{ ss2= new HashSet(); assert 0 }catch(e){ assert e in GroovyCastException }

Map map1= new HashMap(),
    map2= new TreeMap(),
    map3= [:],
    map4= new LinkedHashMap()
assert map1 in HashMap &&
       map2 in TreeMap &&
       [map3, map4].every{ it in LinkedHashMap }

SortedMap sm1= new TreeMap(),
          sm2
try{ sm2= new HashMap(); assert 0 }catch(e){ assert e in GroovyCastException }