Groovy Interfaces and Inheritance
last modified October 15, 2026
Interfaces define a contract: a set of methods that an implementing class must
provide. Inheritance lets a class reuse and extend another class. Groovy supports
both in the same way as Java, and adds named and default parameters that make
method calls easier to read. This tutorial covers interfaces, method overriding
with super, named parameters, and default parameter values.
The basics of abstract classes, @InheritConstructors, and map-style
constructors are covered in the Groovy classes
tutorial. Default parameters for simple functions are covered in the
Groovy functions tutorial. This article focuses
on the interface and parameter patterns that build on them.
Defining and Implementing an Interface
An interface is declared with the interface keyword. Its methods
have no body, so each implementing class must supply them. A class lists the
interfaces it implements after the implements keyword.
interface Shape {
double area()
String name()
default String describe() {
"${name()} with area ${String.format('%.2f', area())}"
}
}
interface Scalable {
void scale(double factor)
}
class Circle implements Shape, Scalable {
double radius
Circle(double radius) { this.radius = radius }
double area() { Math.PI * radius * radius }
String name() { 'Circle' }
void scale(double factor) { radius *= factor }
}
def c = new Circle(2)
println c.describe()
c.scale(2)
println c.describe()
println c instanceof Shape
println c instanceof Scalable
println Shape.isAssignableFrom(Circle)
Shape declares two abstract methods, area and
name, and one method with a body, describe. The
Circle class implements both Shape and
Scalable, so it must provide the abstract methods of both.
default String describe() {
"${name()} with area ${String.format('%.2f', area())}"
}
A default method in an interface has an implementation. Classes
that implement the interface inherit it and can override it. Because
describe calls area() and name(), its
result changes with the state of each object.
println c instanceof Shape println Shape.isAssignableFrom(Circle)
instanceof checks the type of an object at runtime. The
isAssignableFrom method of the Class object does the
same check for classes, which is useful when the types are only known at
runtime.
Groovy does not require the public modifier here, because methods
are public by default, both in interfaces and in classes.
$ groovy Interfaces.groovy Circle with area 12.57 Circle with area 50.27 true true true
Inheritance and Method Overriding
A subclass extends a superclass with the extends keyword. It can
override a method by declaring a method with the same signature. The
super keyword calls the superclass version, either inside an
overridden method or in the constructor.
class Animal {
protected String name
Animal(String name) { this.name = name }
String sound() { '...' }
String speak() { "$name says ${sound()}" }
}
class Dog extends Animal {
Dog(String name) { super(name) }
@Override
String sound() { 'Woof' }
@Override
String speak() { super.speak() + '!' }
}
def animals = [new Animal('Generic'), new Dog('Rex')]
animals.each { println it.speak() }
println Dog.superclass.simpleName
println new Dog('Max') instanceof Animal
Animal.speak calls sound(), which is dispatched at
runtime. When speak runs on a Dog, the overridden
sound returns 'Woof'. This is polymorphism: the same
call produces different results depending on the object's actual class.
Dog(String name) { super(name) }
The super(name) call must be the first statement in the
constructor. It passes the name to the Animal constructor, which
sets the protected field.
String speak() { super.speak() + '!' }
super.speak() runs the Animal version of
speak, and the Dog version adds an exclamation mark.
Extending a method this way avoids copying its code into the subclass.
A class can implement several interfaces, but it can extend only one class. Groovy also supports traits, which combine interface-style contracts with reusable method bodies; they are covered in the Groovy traits tutorial.
$ groovy Inheritance.groovy Generic says ... Rex says Woof! Animal true
Named Parameters with a Map
Groovy lets callers pass arguments by name. When a method has a single
Map parameter, a call such as connect(host: 'x') is
collected into that map. The method reads each key with get, which
accepts a default value for missing keys.
def connect(Map args = [:]) {
def host = args.host ?: 'localhost'
def port = args.get('port', 8080)
def ssl = args.ssl ?: false
"$host:$port ssl=$ssl"
}
println connect()
println connect(host: 'example.com', ssl: true)
println connect(port: 443, host: 'api.example.com')
The parameter has the default [:], an empty map. Without it, a call
with no arguments would pass null and the first property access
would fail. The order of named arguments does not matter.
def port = args.get('port', 8080)
args.get returns the value for port or the fallback
8080 only when the key is missing. The Elvis operator
?: is different: it also replaces falsy values such as
0, false, or an empty string. Use get when
those values are valid input.
The map approach accepts any key, so a misspelled name such as
prot: is silently ignored. Passing a map with unknown keys is also
allowed. If strict checking is required, validate the keys yourself or use the
annotation described in the next section.
$ groovy NamedParams.groovy localhost:8080 ssl=false example.com:8080 ssl=true api.example.com:443 ssl=false
Named Parameters with @NamedVariant
The @NamedVariant annotation generates a method that takes named
arguments, and @NamedParam describes each parameter. It can mark a
parameter as required and give it a default value, and it checks that the
required names are present.
import groovy.transform.NamedVariant
import groovy.transform.NamedParam
@NamedVariant
def createUser(@NamedParam(required = true) String name,
@NamedParam int age = 18,
@NamedParam String role = 'user') {
"$name ($age, $role)"
}
println createUser(name: 'Ann')
println createUser(role: 'admin', name: 'Bob', age: 40)
The method has three parameters, but the caller only has to provide
name. age and role fall back to their
defaults, and the arguments can be given in any order.
@NamedParam(required = true) String name
Marking a parameter as required turns a missing argument into an error that
names the missing key, instead of a null value that fails later.
Calling createUser(age: 3) without name fails with
Missing required named argument 'name'. Use
@NamedVariant for public methods where the caller should get a clear
message, and the plain map approach for quick internal helpers.
$ groovy NamedVariant.groovy Ann (18, user) Bob (40, admin)
Default Parameter Values
A parameter with a default value can be omitted by the caller. Defaults are
written with = after the parameter name. Arguments are matched by
position, so parameters with defaults must come after the required ones.
def greet(String name, String greeting = 'Hello', String punct = '!') {
"$greeting, $name$punct"
}
println greet('Alice')
println greet('Bob', 'Hi')
println greet('Carol', 'Hey', '?')
class Point {
int x, y
Point(int x = 0, int y = 0) { this.x = x; this.y = y }
String toString() { "($x, $y)" }
}
println new Point()
println new Point(5)
println new Point(5, 7)
Each call supplies as many arguments as it needs. Groovy compiles one method
for each number of arguments it can accept, so greet('Alice') and
greet('Alice', 'Hi') both reach the same definition.
Point(int x = 0, int y = 0) { this.x = x; this.y = y }
Default values also work for constructors. new Point() uses
(0, 0), and new Point(5) sets only x.
Default values are evaluated on each call, so an expression such as
List items = [] creates a new list every time the method runs.
A default cannot be skipped by position: to pass a value for
punct, you must also pass a value for greeting, for
example greet('Carol', 'Hello', '?'). Named arguments are not
available for ordinary methods; they work with a Map parameter or
with @NamedVariant, as shown above.
$ groovy Defaults.groovy Hello, Alice! Hi, Bob! Hey, Carol? (0, 0) (5, 0) (5, 7)
Best Practices
- Keep interfaces small. A short contract is easier to implement and to mock in tests.
- Use
superto extend behavior. Call the superclass method and add to its result instead of copying it. - Put defaulted parameters last. This keeps calls readable and avoids surprises when a caller passes a value by position.
- Prefer
@NamedVariantfor public APIs. It documents the parameters, rejects missing required values, and keeps the call site readable. - Use
args.get(key, default)for maps. It makes the fallback explicit and does not replace values that are falsy.
Source
Groovy Object Orientation Documentation
This tutorial covered Groovy interfaces with abstract and default methods,
inheritance with super calls, named parameters through maps and
@NamedVariant, and default parameter values for methods and
constructors.
Author
List all Groovy tutorials.