Classes
A type of your own: properties that come with getters and setters for free, a constructor that fills them in, methods that use them, and a toString that decides what printing means.
Properties are fields with manners
A class declares its data as properties: String title, int pages. Writing that one line gives you the field, a getter, a setter and the constructor plumbing — the getTitle() a Java programmer would write by hand already exists, and so does book.title = 'Dune'.
The class is the answer to a naming problem: three variables called title, author and pages that always travel together are one thing with a name, and once it has a name it can be passed to a method, collected in a list and printed without repeating its shape everywhere. The getter the compiler writes is a real method, so a subclass can override it and every read of the property goes through the override.
Constructors say what is required
A constructor has the class name and no return type: Book(String title, String author, int pages). Inside, this.title = title is the standard way to tell the property from the parameter of the same name. After that, no Book can exist half-built — which is the point of taking the arguments at all.
Groovy will also build a map constructor — new Book(title: 'Dune', pages: 412) — when you have not written a constructor of your own. Both styles are common in Groovy code; pick one per class and stay with it, because a class with both habits is a class whose reader has to check. A constructor that takes its values is a promise that the object is usable the moment it exists; a no-argument constructor plus setters makes that promise later and weaker.
Methods, and what printing means
Methods inside a class can reach its properties by name, with no this. and no ceremony: title + ' by ' + author. Calling one uses the dot: book.describe().
toString() is the method the world calls when it needs the class as text — println calls it, string interpolation calls it, a list of books calls it for every element. Without it you get something like Book@1b6d3586, which is an address, not an answer. Writing toString is the cheapest thing you can do to make a class usable in output. Keep it stable and readable rather than a summary of every field: debuggers, logs and error messages you did not write will all call it.
A class that counts itself
class Counter {
int value = 0
void bump() {
value = value + 1
}
int getDoubled() {
value * 2
}
String toString() {
'Counter(' + value + ')'
}
}
def counter = new Counter()
counter.bump()
counter.bump()
println counter.value
println counter.doubled
println counterWhat the property line generates
String title inside a class is not just a field. Groovy generates a private field and a public getter and setter, and then treats book.title as a call to them. That is why a property can be read and assigned without writing either method, and why a class from Java with getTitle() also answers book.title.
You can still write the getter yourself when reading needs logic — a computed label, a guard, a formatted value — and your method replaces the generated one. For a value that must not change from outside, declare a private field and a getter rather than a property; that is the explicit way to make it read-only.
The map constructor, and equality
When a class declares no constructor, Groovy adds a no-argument one and a map constructor, so new Book(title: 'Dune', pages: 412) fills the properties by name. Once you write a constructor of your own, those generated ones are gone; the map form then fails, which is a common surprise when a class grows up.
Objects compare by identity unless the class says otherwise. Two Books with the same title are == only if equals is written, and a HashSet or map key needs hashCode to agree with it. For a small data class, toString makes output readable; equals and hashCode make the objects behave like values.
Three class mistakes: in a constructor, title = title assigns the parameter to itself, so write this.title = title; printing an object without toString shows something like Book@1b6d3586, which is an address rather than data; and two objects with equal fields are not equal by default, because == falls back to identity until you write equals.
Try these next
- Add a
getSummary()to the class and read it as a property. - Create two books with the same data and compare them with
==. - Add a static counter that increments in the constructor and print it from the class name.
One book, four ways to look at it
Run it and read the property, the method, the printed object and the changed page count. Then add a `summary()` method that returns a one-line description, and print it.
Two books and a shared count
Run it and check that both books increased the count. Then add a third book, and a method that answers how many pages the two longest books have together.
getDoubled() is reachable as counter.doubled because Groovy treats a getter as a property. Both spellings work; the property form reads better in a line, the method form makes it obvious that work is happening.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does declaring String title inside a class give you?
