Object-Oriented Programming (OOP)

Groovy support Object-Oriented Programming (OOP) and mostly is similar to Java, there are some Groovy specifics and i will mention those we go through the section. This is only going to be a quick brief over of OOP and will add to this section some of the more complex features of OOP in Groovy.

Classes, Fields and Local Variables

Groovy tries to eliminate a lot of the boilerplate code for example there are defaults for access modifiers which will be used unless you override those, also getters and setters are automatically generated if you don't specify a access modifier.

Class example
class Person {

    String firstName                        // default is private, plus getters and setters are created
    String lastName

    def dob                                 // no type assigned (actually it's an Object)

    // private | protected | public         // normal Java access modifiers, but if used no getters/setters will be created
    protected String f1, f2
    private Date createdOn = new Date()

    // static and static final same as Java
    static welcomeMsg = 'HELLO STATIC'
    public static final String WELCOME_MSG = 'HELLO'          // you cannot change, its final, its know asa constant

    // local variables
    def foo() {
        String msg = 'Hello'               // Local variable to this method
        String firstName = 'Paul'

        println "$msg, $firstName"
        println "$welcomemsg"              // can use class variable
    }
}

println Person.welcomeMsg                  // use static field
println Person.WELCOME_MSG                 // use static final field

def p1 = new Person ()
p1.foo()

Contructors and Methods

Constructors and methods again are very similar to Java, you can create contructor which can initialize a new object, you can also override. Methods can use access modifiers and return types, however you can use the def keyword to return any type.

Constructors and Methods
@groovy.transform.ToString
class PersonM1 {
    String firstName, lastName

    PersonM1(String first, String last){
       firstName = first
       lastName = last
    }

    // Constructor - overrides default constructor
    PersonM1(String fullName){
        List parts = fullName.split(" ")
        firstName = parts[0]
        lastName = parts[1]
    }

    // methods
    public void foo(String a, String b){
        // do stuff
    }

    def foo2(String a, String b) {                         // def means return anything we don't care, this could replace above
    }

    static String doGoodWork(){                            // static methods is the same as Java
        println "doing good work"
    }

    // You can use defaults to method arguments
    List someMethod(List numbers = [1,2,3], Boolean canAccessAll = false ){

    }

    def concat(String... args) {                         // use the varargs which is same in Java
        println args.length
    }

}

// default constructor will take parameters
PersonM1 p1 = new PersonM1("Paul", "Valle")
println p1

// default constructor breaks when you overload the constructors, so below fails
// PersonM1 p2 = new PersonM1(firstName: "Paul", lastName: "Valle")
// println p2

PersonM1 p3 = new PersonM1("Lorraine Valle")
println p3

PersonM1.doGoodWork()

PersonM1 p = new PersonM1("Paul Valle")
p.concat('a','b','c','d')

Packages

Packages are the same as in Java, Packages are directory structures used to organize classes and interfaces, Packages provide a mechanism for software reuse, they also provide a convention for unique class names. When coding for a large project, sometimes classes can be called the same name, especially if third party developers are involved by using packages you can distinguish between class names. Normally you supply your domain name as a package location.

Package example
package uk.co.datadisk.groovy.entities

class Person {

    String firstName        // default is private, plus getters and setters are created
    String lastName
    
    ...
}

Inheritance

Inheritance is the same as in Java, you use the extends keywords to extend a class, you can override methods just like Java.

// Phone.groovy
@groovy.transform.ToString
class Phone {

    String name
    String os
    String appStore

    def powerOn(){
        println "Power On"
    }

    def powerOff(){
        println "Power Off"
    }

    def ring(){
        println "Ring, Ring, Ring"
    }
}

// Iphone.groovy
class Iphone extends Phone {                     // use the extends keyword

    String iosVersion

    def airPlay(){
        println "Connect Air Play"
    }

    def powerOn(){                               // override the super class powerOn() method
        println "Turn on iphone"
    }
}

// Groovy inheritance is same as Java
Phone iphone = new Iphone(name: "6c", appStore: "MK", os: "ios")
println iphone
iphone.powerOn()
iphone.ring()
iphone.airPlay()

Interfaces

Interfaces again are the same as Java, you use the implements keyword

// IPeopleService.groovy
interface IPeopleService {
    List findAll()
    People find()
}

// PeopleService.groovy
class PeopleService implements IPeopleService {

    @Override
    List findAll() {
        People p1 = new People(firstName: "Paul", lastName: "Valle")
        People p2 = new People(firstName: "Lorraine", lastName: "Valle")
        return [p1,p2]
    }

    @Override
    People find() {
        People p1 = new People(firstName: "Paul", lastName: "Valle")
        return p1
    }
}

// People.groovy
@groovy.transform.ToString
class People {
    String firstName, lastName
}

// InterfaceApp.groovy
PeopleService ps = new PeopleService()
println ps.find()
println ps.findAll()

Traits

Traits are reusable components representing a set of methods or behaviors that we can use to extend the functionality of multiple classes. For this reason, they're considered as interfaces, carrying both default implementations and state. All traits are defined using the trait keyword. Traits are a lot like Java 8 interfaces (default methods) but one big difference is that a trait can contain state information.

Traits example
// FlyingAbility.groovy
trait FlyingAbility {

    String fly(){                         // Interface cannot have a body (unless default method), a trait can
       "I'm flying"                       // no need for return statement in Groovy
    }

    abstract String foo()

    private String bar() {
        "bar"
    }

    String whoWins() {
        "Flying ability wins!!!!"
    }
}

// SpeakingAbility.groovy
trait SpeakingAbility {

    String speak1                             // a trait can hold state information
    int weight = 1
    private String speak2

    String speak(){
       "I'm speaking"
    }

    String whoWins() {
        "Speaking ability wins!!!!"
    }
}

// Bird.groovy
class Bird implements FlyingAbility, SpeakingAbility {

    @Override                                 // you can use @override annotation
    String foo() {
        return "foo method"
    }
}


// traitsApp.groovy
Bird bird = new Bird()
println bird.fly()
println bird.speak()
println bird.foo()
//println bird.bar()                         // cannot use as its private

bird.speak1 = "Hello"
bird.weight = 2
println bird.speak1 + " weight: " + bird.weight

println bird.whoWins()                       // if have same method with same name, last trait in implements wins

Groovy Beans

Beans in either Groovy (or Java) is more of a standard and is not a type, we use encapsulation to protect the private fields and then use getter/setters to access those private fields. Also a public no-argument constructor is created and the class implements Serializable.

Bean example
// EmployeeBean.java
public class EmployeeBean implements Serializable {

    // private properties
    private String firstName;
    private String lastName;
    private String email;

    // public no-arg constructor
    public EmployeeBean() {
    }

    // getters & and setters
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    // toString
    @Override
    public String toString() {
        return "EmployeeBean{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }
}

// Employee.groovy                                         // Groovy uses alot less code than Java one above
@groovy.transform.ToString
class Employee implements Serializable {

    // this is the same as Java EmployeeBean file

    // these are private thus getters and setters are automatically created (no access modifiers)
    String firstName, lastName, email

    String fullName

    void setFullName(String name){
        fullName = name
    }

    void getFullName(){
        "Full Name: ${fullName}"
    }

}

// DoubleBean.groovy
class DoubleBean {

    public Integer value

    void setValue(value){
        this.value = value
    }

    Integer getValue(){
        value * 2
    }
}

// app.groovy
Employee emp = new Employee(firstName: "Paul", lastName: "Valle", email: "paul.valle@example.com")

println emp

Employee emp1 = new Employee()
emp1.firstName = "Will"                             // use the setter method
emp1.lastName = "Hay"
emp1.fullName = "Will Hay"
emp1.email = "will.hay@example.com"
println emp1

println emp1.fullName                               // use the getter method

DoubleBean db = new DoubleBean()
db.value = 100                                      // use the setValue method

println db.value                                    // calling the getValue method
println db.@value                                   // call the actual value member field (notice the @), don't use the getter