Groovy enables one class to extend another, just as interfaces can, though classes extend at most one class. We can test for extended classes with the 'in' operator, just like with implemented interfaces:
class A{}
class B extends A{}
def b= new B()
assert b in B && b in A
class A1{}
class A2{}
//class C extends A1, A2{}
//compile error when uncommented: a class can extend at most one class
|
Public instance fields, properties, and methods defined on an extended class are also available on the extending class:
class A{
public int prev //field
int signature //property
String sayPies(int n){ "There are ${prev= signature= n} pies!" } //method
}
class B extends A{
String sayBirds(int n){ "There are $n birds!" }
}
def b= new B()
assert b.sayBirds(17) == 'There are 17 birds!'
assert b.sayPies(11) == 'There are 11 pies!'
//method sayPies(int) from A acts as part of B
assert b.prev == 11 //field 'prev' from A acts as part of B
b.signature= 19
assert b.signature == 19 //property 'signature' from A acts as part of B
assert b.getSignature() == 19
|
We can use the 'private' and 'protected' modifiers to restrict the visibility of instance methods, properties, and fields:
class A{
//private methods, properties, and fields are not visible outside the class,
//even in inheriting classes...
private int prevPies
private String sayPies(int n){ "There are ${prevPies= n} pies!" }
//protected methods, properties, and fields are visible in inheriting
//classes (and within the same package)...
protected int prevBeans
protected String sayBeans(int n){ "There are ${prevBeans= n} beans!" }
}
class B extends A{
def testAccesses(){
assert sayPies(23) == 'There are 23 pies!'
//Groovy bug: this private method shouldn't be visible here
try{ prevPies; assert 0 }catch(e){ assert e in MissingPropertyException }
//A's private field 'prevPies' not visible here
assert sayBeans(29) == 'There are 29 beans!'
//A's protected method visible here in an inheriting class
assert prevBeans == 29
//A's protected field visible here in an inheriting class
}
}
def b= new B()
assert b.sayPies(11) == 'There are 11 pies!'
//Groovy bug: this private method shouldn't be visible here
try{ b.prevPies; assert 0 }catch(e){ assert e in MissingPropertyException }
//A's private field 'prevPies' not visible here
assert b.sayBeans(14) == 'There are 14 beans!'
//this protected method is visible here in the same package it's defined in
assert b.prevBeans == 14
//this protected field is visible here in the same package it's defined in
b.testAccesses()
|
Public static fields, properties, and methods are inherited by extending classes:
class A{
static public int numBananas //field
static signature //property
static String sayBananas(int n){ //method
"There are ${numBananas= signature= n} bananas!"
}
}
class B extends A{}
assert A.sayBananas(23) == 'There are 23 bananas!' //method call
assert A.numBananas == 23 //field access
assert A.signature == 23 //property accesses
assert A.getSignature() == 23
assert B.sayBananas(23) == 'There are 23 bananas!' //method call
assert B.numBananas == 23 //field access
assert B.signature == 23 //property access
B.getSignature() == 23 //property access using method syntax
|
We can make static methods, properties, and fields private or protected:
class A{
static private int numBananas= 0
static private String sayBananas(int n){
"There are ${numBananas= n} bananas!"
}
static protected int numApples= 0
static protected String sayApples(int n){
"There are ${numApples= n} apples!"
}
}
class B extends A{
static testAccesses(){
assert sayBananas(37) == 'There are 37 bananas!'
//numBananas //compile error when uncommented:
//A's private field not visible here
assert sayApples(29) == 'There are 29 apples!'
numApples //A's protected field visible here in an inheriting class
}
}
assert B.sayBananas(31) == 'There are 31 bananas!'
try{ B.numBananas; assert 0 }catch(e){ assert e in MissingPropertyException }
assert B.sayApples(23) == 'There are 23 apples!'
assert B.numApples == 23
B.testAccesses()
|
We can define what's called an "abstract class", a class with only some methods defined, the others being only declarations just like in interfaces. An abstract class and each method declaration in it must be modified with the keyword 'abstract':
interface X{
def x()
}
interface Y{
def y()
}
abstract class A{
def a(){ println 1 } //method definition
abstract b() //declaration of method only
}
class B extends A implements X, Y{
def x(){ println 2 }
def y(){ println 3 }
def b(){ println 4 } //declared method from abstract class A defined here
}
|
Whether a method is static or not is part of its definition, not its declaration. So interface and abstract methods may not be declared static.
interface X{
def x()
//static x1() //error when uncommented: interface methods can not be static
}
interface Y{
def y()
}
abstract class A{
static a(){ println 1 }
abstract b()
abstract c()
//abstract static c1()
//error when uncommented: abstract methods can not be static
}
class B extends A implements X, Y{
static x(){ println 2 }
def y(){ println 3 }
static b(){ println 4 }
def c(){ println 5 }
}
|
At the other end from abstract classes and methods are "final classes" and "final methods". A final class may not be extended; a final method may not be overriden:
class A{
final a(){ 11 }
def b(){ 12 }
}
final class B extends A{
//def a(){ 15 } //compile error when uncommented: can not override final A.a()
def b(){ 16 }
}
//class C extends B{} //compile error when uncommented: can not extend final C
|
Just as a class's constructor can call another constructor at the beginning of its code, so also it can call a constructor on the superclass at the beginning of its code:
class A{
def list= []
A(){
list<< "A constructed"
}
A(int i){
this()
list<< "A constructed with $i"
}
}
class B extends A{
B(){
list<< "B constructed"
}
B(String s){
super(5) //a constructor can call its superclass's constructor if it's
//the first statement
list<< "B constructed with '$s'"
}
}
def b1= new B('kea')
assert b1.list.collect{it as String} == [
"A constructed",
"A constructed with 5",
"B constructed with 'kea'",
]
def b2= new B()
assert b2.list == [
"A constructed",
//default parameterless constructor called if super() not called
"B constructed",
]
|
Some classes supplied with Groovy are intended to be extended to be used. For example, FilterInputStream, FilterOutputStream, FilterReader, and FilterWriter:
//When not extended, FilterOutputStream simply passes its method calls to the
//wrapped stream...
try{
def fos= new FilterOutputStream(new FileOutputStream('abc.txt'))
fos.write(33i)
fos.write([34,35,36] as byte[])
fos.write([34,35,36,37,38,39,40] as byte[], 3, 2)
fos.close()
def fis= new FilterInputStream(new FileInputStream('abc.txt'))
def ba= new byte[6]
fis.read(ba)
assert ba.toList() == [33,34,35,36,37,38]
}
//We can extend FilterOutputStream to provide the logic for the filter...
class EvenNumberOutputStream extends FilterOutputStream{
EvenNumberOutputStream(OutputStream out){
super(out)
}
def write(int i){
if(i%2 == 0) super.write(i) //call method of same name in the super-class
}
def write(byte[] ba){
super.write( ba.toList().findAll{ it%2 == 0 } as byte[] )
}
def write(byte[] ba, int start, int size){
this.write( ba[start..<(start+size)] as byte[] )
//another way to call method of same name in same class definition
}
}
try{ //...then call the methods...
def fos= new EvenNumberOutputStream(new FileOutputStream('abc.txt'))
fos.write(33i)
fos.write([34,35,36] as byte[])
fos.write([34,35,36,37,38,39,40] as byte[], 3, 2)
fos.close()
def fis= new FilterInputStream(new FileInputStream('abc.txt'))
def ba= new byte[6]
fis.read(ba)
assert ba.toList() == [34,36,38,0,0,0]
}
|
We can similarly extend FilterInputStream, FilterReader, and FilterWriter.
All classes are arranged in a hierarchy with java.lang.Object as the root. Here are those we've met so far; those labelled as such are abstract and final classes:
java.lang.Object
java.lang.Boolean (final)
java.lang.Character (final)
java.lang.Number (abstract)
java.lang.Integer (final)
java.lang.Long (final)
java.math.BigInteger
java.math.BigDecimal
java.lang.Short (final)
java.lang.Byte (final)
java.lang.Float (final)
java.lang.Double (final)
java.math.MathContext (final)
java.util.Random
java.util.Date
java.util.TimeZone (abstract)
java.util.SimpleTimeZone
java.util.Calendar (abstract)
java.util.GregorianCalendar
groovy.time.BaseDuration (abstract)
groovy.time.Duration
groovy.time.TimeDuration
groovy.time.DatumDependentDuration
groovy.time.TimeDatumDependentDuration
java.util.AbstractCollection (abstract)
java.util.AbstractList (abstract)
java.util.ArrayList
groovy.lang.Sequence
groovy.lang.IntRange
groovy.lang.ObjectRange
java.util.AbstractSet (abstract)
java.util.HashSet
java.util.TreeSet
java.util.AbstractMap (abstract)
java.HashMap
java.util.LinkedHashMap
groovy.lang.SpreadMap
java.TreeMap
java.util.Collections
java.lang.String (final)
java.lang.StringBuffer (final)
java.util.regex.Pattern (final)
java.util.regex.Matcher (final)
groovy.lang.GroovyObjectSupport (abstract)
groovy.lang.Binding
groovy.lang.Closure (abstract)
groovy.lang.GString (abstract)
groovy.util.Expando
java.text.Format (abstract)
java.text.NumberFormat (abstract)
java.text.DecimalFormat
java.text.DateFormat (abstract)
java.text.SimpleDateFormat
java.text.DecimalFormatSymbols
java.text.DateFormatSymbols
java.io.File
java.io.InputStream (abstract)
java.io.ByteArrayInputStream
java.io.FileInputStream
java.io.FilterInputStream
java.io.BufferedInputStream
java.io.DataInputStream
java.io.LineNumberInputStream
java.io.PushbackInputStream
java.io.SequenceInputStream
java.io.StringBufferInputStream
java.io.OutputStream (abstract)
java.io.ByteArrayOutputStream
java.io.FileOutputStream
java.io.FilterOutputStream
java.io.BufferedOutputStream
java.io.DataOutputStream
java.io.PrintStream
java.io.Reader (abstract)
java.io.BufferedReader
java.io.LineNumberReader
java.io.CharArrayReader
java.io.FilterReader (abstract)
java.io.PushbackReader
java.io.InputStreamReader
java.io.FileReader
java.io.StringReader
java.io.Writer (abstract)
java.io.BufferedWriter
java.io.CharArrayWriter
java.io.FilterWriter (abstract)
java.io.OutputStreamWriter
java.io.FileWriter
java.io.PrintWriter
java.io.StringWriter
|