DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

SBOMs are essential to circumventing software supply chain attacks, and they provide visibility into various software components.

Related

  • The Unreasonable Effectiveness of the Actor Model for Creating Agentic LLM Applications
  • Why and How To Integrate Elastic APM in Apache JMeter
  • Transitioning From Groovy to Kotlin for Gradle Android Projects
  • Streamlining Your Workflow With the Jenkins HTTP Request Plugin: A Guide to Replacing CURL in Scripts

Trending

  • Maximizing Productivity: GitHub Copilot With Custom Instructions in VS Code
  • The OWASP Top 10 for LLM Applications: An Overview of AI Security Risks
  • Top Tools for Front-End Developers
  • Exploring Data Redaction Enhancements in Oracle Database 23ai
  1. DZone
  2. Coding
  3. Languages
  4. Groovy Closures Do Not Have Access to Private Methods in a Super Class

Groovy Closures Do Not Have Access to Private Methods in a Super Class

By 
Nirav Assar user avatar
Nirav Assar
·
Aug. 26, 12 · Interview
Likes (0)
Comment
Save
Tweet
Share
11.9K Views

Join the DZone community and get the full member experience.

Join For Free
Recently I came upon a groovy oddity. (At least it is perceived by me to be an oddity). Closures in a groovy class do not have access to a private method if that method is defined in the superclass. This seems odd paired against the fact that regular methods in a super class can access private method defined in the super class

Background

Groovy closures have the same scope access to class member variables and methods as a regular groovy method. In other words, closures are bound to variables in the scope they are defined. See the codehaus link for the official documentation:

http://groovy.codehaus.org/Closures

This implies that a closure will play by the rules of Object Orientation in the Java language. However, I found that closures do not have access to private methods that are defined in a super class.

The best way to demonstrate is through a short example from Grails. I have used TDD for the example. Note this is a dummy case with no business purpose. Later on I will offer a more reasonable scenario in the business context where I encountered this scenario.


Simple Groovy Example

Take a class that has a closure, a public method, and a private method. Then extend that class. Try and invoke the closure. We get an error.
package closure.access

class SendCheckService {

  def calendarService

  /**
   * Closure that invokes a private method
   */
  def closureToSendCheck = {
    sendPersonalCheck()
  }

  def regularMethod() {
    sendPersonalCheck()
  }

  /*
   * Private method that we want to see executed
   */
  private sendPersonalCheck() {
    println "Sending Personal Check"
  }
}

class FooService extends SendCheckService {

}
Here are some tests the demonstrate the error.
package closure.access

import grails.test.*

class SendCheckServiceTests extends GrailsUnitTestCase {

  def sendCheckService

  protected void setUp() {
    super.setUp()
    sendCheckService = new SendCheckService()
  }

  /**
   * Invocation of the closure from the super class prints out the message:
   * "Sending Personal Check"
   */
  void testClosureToSendCheck() {
    sendCheckService.closureToSendCheck()
  }

  /**
   * Invocation of the method from the super class prints out the message:
   * "Sending Personal Check"
   */
  void testRegularMethod() {
    sendCheckService.regularMethod()
  }

  /**
   * Invocation of the the closure from the subclass yields an error:
   * groovy.lang.MissingMethodException: No signature of method: closure.access.FooService.sendPersonalCheck()
   *   is applicable for argument types: () values: []
   * This essentially means it does not exists.
   */
  void testFoo_ClosureToSendCheck() {
    def fooService = new FooService()
    fooService.closureToSendCheck()
  }

  /**
   * Invocation of the method does not yield and error!
   */
  void testFoo_RegularMethod() {
    def fooService = new FooService()
    fooService.regularMethod()
  }
} 
All is Well
  • testClosureToSendCheck() executes fine where the closure can access the private method. This is as expected. It is all well and good because we have not extended class yet.
  • testRegularMethod() just demonstrates regular OO principles. A method is able to invoke private methods.
Not as Expected
  • testFoo_ClosureToSendCheck() invokes a subclass of SendCheckService. It calls the same closure, yet we get served up a MissingMethodException.
  • testFoo_RegularMethod() just contrasts testFoo_ClosureToSendCheck(). I invoked this test to show that we should be able to have closures access private methods because other regular methods can!
Why Even Try to Have a Closure Call A Private Method

This may be a double loop learning question any intelligent developer might ask. It questions why we need to even get into this mess. This is a valid point and should be explained.

It is optimal to use closures in an attempt to reuse existing template logic. Let me give a simple business problem.

Imagine we need to code a system that sends out various types of checks: personal checks and business checks. We have the stipulation that these two events must NEVER be done together. Only send a personal check at one instance, and send a business check at another time. However, they both need to follow the same logic. They must be sent on a business day (no holidays or weekends). Thus, we have a scenario where they need the same calendar logic, but it is needed separately.

Duplicate Logic

We could just code the calendar logic twice. (Remember sending business checks and personal checks together in one request cannot occur!)
package closure.access

class SendCheckService2 {

  def calendarService

  def triggerPersonalCheck () {
    if (calendarService.todayIsBusinessDay()) {
      sendPersonalCheck()  
    } else {
      println "DO NOTHING"
    }
  }

  def triggerBusinessCheck() {
    if (calendarService.todayIsBusinessDay()) {
      sendBusinessCheck()
    } else {
      println "DO NOTHING"
    }
  }

  private sendPersonalCheck() {
    println "Sending Personal Check"
  }

  private sendBusinessCheck() {
    println "Sending Business Check"
  }  
}
Use the DRY Principle

In order to avoid this and follow DRY, we can use closures. Create a method that accepts a closure, and pass it the code snippets to execute in a closure. As a result we have the calendar logic defined once, but executed separately upon a different code snippet.
package closure.access

class SendCheckService3 {

  def calendarService

  def triggerPersonalCheck() {
    checkIfBusinessDayAndExecute(sendBusinessCheck)
  }

  def triggerBusinessCheck() {
    checkIfBusinessDayAndExecute(sendBusinessCheck)
  }

  def checkIfBusinessDayAndExecute(Closure closure) {
    if (calendarService.todayIsBusinessDay()) {
      closure()
    } else {
      println "DO NOTHING"
    }
  }

  /**
   * This is now a closure we can pass around
   */
  def sendPersonalCheck = {
    println "Sending Personal Check"
  }

  /**
   * This is now a closure we can pass around
   */  
  def sendBusinessCheck = {
    println "Sending Business Check"
  }  
}
In my real world scenario where I encountered the closure issue, it happened that my closure was trying to execute a private method in an abstract class. This is where I observed the problem.

JVM Thoughts

I honestly do not know the gory details behind why closures in super classes cannot access private methods, but I have an idea. Groovy creates closures by compiling them as inner classes. Since the subclass extends the superclass and then contains a closure, the inner class does not have access to the super class' methods. The reason why a method has access, is because it is compiled as one instance of the class. The closure implementation is not that way (being a inner class), and thus this is why we see a violation of Object Oriented behavior.

If you have a better explanation or futher knowledge of the details behind this issue, please describe them in the comments. Thanks for taking the time to delve in this area.

Thanks to Scott Risk for helping with examples.
Groovy (programming language)

Published at DZone with permission of Nirav Assar, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • The Unreasonable Effectiveness of the Actor Model for Creating Agentic LLM Applications
  • Why and How To Integrate Elastic APM in Apache JMeter
  • Transitioning From Groovy to Kotlin for Gradle Android Projects
  • Streamlining Your Workflow With the Jenkins HTTP Request Plugin: A Guide to Replacing CURL in Scripts

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: