Groovy Operators

Control Statements

Groovy File Handling

Groovy Error & Exceptions

Groovy Multithreading

Groovy Synchronization

Groovy - Implementing Multiple Traits



In Groovy, we can implement Traits in the same fashion as we can implement multiple interfaces but traits provides more flexibility and benefits.

Syntax

trait Trait1 {}
trait Trait2 {}
trait Trait3 {}

class Multi implements Trait1, Trait2, Trait3 {}

Example - Implementing Multiple Traits

In this example, we're creating two traits to enable logging and save capability.

Create Traits

// in actual application, we can log the message in log file
trait Loggable {
   void log(String message) {
      println "logging: ${message}"
   }
}

// in actual application, we can save data on disk/network and so
trait Savable {
   void save(Object data) {
      println "Saving: ${data}"
   }
}

Create a class implementing both Traits

Class MyReport implements Loggable, Savable {
   String title
   String content
   
   MyReport(String title, String content) {
      this.title = title
      this.content = content
   }

   void prepareReport() {
      // log is inherited from Loggable trait
      log("Preparing report: ${title}")
      println "Report '${title}' prepared successfully."
   }

   void saveReport() {
      log("Persisting report: ${title}")
      // save method is inherited from Savable trait
      save(this) // Save the report object itself
   }
}

Use the Class

def salesReport = new MyReport("Daily Sales Details", "Total sales: $1000. Key products: TV, Mobile, Charger.")

salesReport.prepareReport()
salesReport.saveReport()

// trait methods can be accessed directly as well
salesReport.log("Report Analyzed")
salesReport.save("Report Sent.")

Complete Example

Example.groovy

// in actual application, we can log the message in log file
trait Loggable {
   void log(String message) {
      println "logging: ${message}"
   }
}

// in actual application, we can save data on disk/network and so
trait Savable {
   void save(Object data) {
      println "Saving: ${data}"
   }
}

class MyReport implements Loggable, Savable {
   String title
   String content
   
   MyReport(String title, String content) {
      this.title = title
      this.content = content
   }

   void prepareReport() {
      // log is inherited from Loggable trait
      log("Preparing report: ${title}")
      println "Report '${title}' prepared successfully."
   }

   void saveReport() {
      log("Persisting report: ${title}")
      // save method is inherited from Savable trait
      save(this) // Save the report object itself
   }
}

def salesReport = new MyReport("Daily Sales Details", "Total sales: \$1000. Key products: TV, Mobile, Charger.")

salesReport.prepareReport()
salesReport.saveReport()

// trait methods can be accessed directly as well
salesReport.log("Report Analyzed")
salesReport.save("Report Sent.")

Output

When we run the above program, we will get the following result.

logging: Preparing report: Daily Sales Details
Report 'Daily Sales Details' prepared successfully.
logging: Persisting report: Daily Sales Details
Saving: MyReport@2ca923bb
logging: Report Analyzed
Saving: Report Sent.

Key Considerations

  • Conflict Resolution − In case, a class implementing the trait is having same method signature, the class method will take the precedence over the trait's one. Whereas if two traits are having same method signature than the rightmost trait takes the precedence. Another way to resolve conflict is to use trait name before trait method as trait-name.trait-method.

  • Composibility − By implementing functionalities in multiple traits and by implementing these traits, we can allow a class to have multiple functionalities.

  • Avoiding Diamond Problem of Multiple Inheritance − Diamond problem refers to having same method in two super classes and thus subclass has ambiguity in using such method if both super classes are extended. Traits solves this problem by providing automatic conflict resolution and method precedence.

Advertisements