Classes and Objects
Prerequisites: Methods
Groovy classes compile to ordinary Java classes, but need far less code.
class Person {
String name
int age
String greet() {
"Hi, I'm $name and I'm $age."
}
boolean isAdult() { age >= 18 }
}
def ada = new Person(name: 'Ada', age: 36)
println ada.greet()
println ada.name
ada.age = 37
println ada.getAge()
println ada.adult
Hi, I'm Ada and I'm 36.
Ada
37
true
What Groovy did for you:
String namewithout a modifier is a property: a private field plus a publicgetName()andsetName().ada.namecalls the getter;ada.age = 37calls the setter.- Classes and methods are
publicby default. - A named-argument constructor (
new Person(name: ..., age: ...)) is available whenever the class has a no-argument constructor. isAdult()can be read as the propertyadult.
Constructors and custom accessors
class Temperature {
final BigDecimal celsius
Temperature(BigDecimal celsius) {
if (celsius < -273.15) throw new IllegalArgumentException('below absolute zero')
this.celsius = celsius
}
BigDecimal getFahrenheit() { celsius * 9 / 5 + 32 }
String toString() { "${celsius}°C" }
}
def t = new Temperature(21.5)
println t
println t.fahrenheit
try {
new Temperature(-300)
} catch (IllegalArgumentException e) {
println "Error: $e.message"
}
21.5°C
70.7
Error: below absolute zero
- A
finalproperty gets a getter but no setter. - A
getX()method creates a read-only computed propertyx. - Overriding
toString()controls how the object prints.
Static members and fields
class Counter {
static int created = 0
private int value = 0 // an explicit modifier makes a plain field
Counter() { created++ }
Counter increment() { value++; this }
int getValue() { value }
}
def c = new Counter().increment().increment()
new Counter()
println c.value
println Counter.created
2
2
Returning this makes methods chainable. Adding private,
protected, or public to a field turns off property generation.
Equality and hashing
class Point {
int x, y
boolean equals(Object o) { o instanceof Point && o.x == x && o.y == y }
int hashCode() { Objects.hash(x, y) }
String toString() { "($x, $y)" }
}
def p = new Point(x: 1, y: 2)
def q = new Point(x: 1, y: 2)
println p == q
println p.is(q)
println([p, q].toSet())
true
false
[(1, 2)]
Writing equals, hashCode, and toString by hand is tedious — the
AST transformations lesson shows how one annotation generates all
three.
Practice
Write a BankAccount class with an owner, a read-only balance, and
deposit and withdraw methods that reject invalid amounts.
Show solution
class BankAccount {
final String owner
private BigDecimal balance = 0
BankAccount(String owner) { this.owner = owner }
BigDecimal getBalance() { balance }
void deposit(BigDecimal amount) {
if (amount <= 0) throw new IllegalArgumentException('amount must be positive')
balance += amount
}
void withdraw(BigDecimal amount) {
if (amount > balance) throw new IllegalStateException('insufficient funds')
balance -= amount
}
}
def acct = new BankAccount('Ada')
acct.deposit(100)
acct.withdraw(30.25)
println "$acct.owner has $acct.balance"
try {
acct.withdraw(500)
} catch (IllegalStateException e) {
println e.message
}
Ada has 69.75
insufficient funds