Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
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

...

Code Block
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}

...

Code Block
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 }

...

Code Block
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

...

Code Block
//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:

Code Block
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:

Code Block
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:

Code Block
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 assert  a.class == A && a.toString() == 'x: 1; y: 2'

Statically-Typed Arrays

We can statically type an Object array variable:

Code Block
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] }

...

Code Block
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

...

Code Block
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

...

Code Block
//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:

Code Block
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:

Code Block
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:

Code Block

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:

Code Block
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:

Code Block
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:

...

Code Block
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 Lists and SetsCollections, and the Iterator interface in the tutorial on Control Structures.

...

Code Block
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 }