Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a Groovy method truly protected

Tags:

groovy

Trying to make a method in groovy protected:

package com.foo

class Foo {

  protected def getSomething(){
  }
}

This doesn't work since groovy by default makes pretty much everything visible, so I tried using @PackageScope

package com.foo

import groovy.transform.PackageScope    

@PacakgeScope
class Foo {

  def getSomething(){
  }
}

This sort of works, but only if the caller uses @CompileStatic...

   package com.bar

   class Bar {

   @CompileStatic
   static void main(args){
      def f = new Foo()
      println f.getSomething() 
   }

The above throws IllegalAccessError, that's nice, but without @CompileStatic, no error is generated; not so nice. I can't force users to compile statically, so is there any alternative to enforce protected methods?

From Groovy Documentation

Protected in Groovy has the same meaning as protected in Java, i.e. you can have friends in the same package and derived classes can also see protected members.

Ok, if protected has the same meaning in Groovy but isn't enforced as such, doesn't that erode its meaning? Maybe I'm missing something,

like image 431
raffian Avatar asked Jul 05 '14 00:07

raffian


2 Answers

Short answer: Groovy does not enforce visibility checks.

Longer answer Protected has a meaning in Java, that you surely know. I mention it only for the interested reader: http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6.2

It is not that Groovy does not set the same modifier. So seen from Java the member will be protected just as in Java itself. It is more that Groovy does not perform the visibility check at runtime (or compile time) and might even use reflection to force accessibility. Groovy has to do so, because in general in Groovy the class accessing the member is one of the runtime. That means Groovy would have to emulate visibility checks at runtime, but for this some kind of "origin of call" is required, but not always available in Groovy due to the meta object protocol lacking the ability to properly transfer it.

Using @CompileStatic things are different. Here a direct access to the member is produced. Only it should have failed compilation already and not fail at runtime with IllegalAccessError.

like image 154
blackdrag Avatar answered Oct 27 '22 18:10

blackdrag


This is quite head-scratching. I just did a search on "groovy information hiding". There's hardly anything out there!

There may be techniques involving closures to obtain information-hiding in Groovy. But I just did a few experiments and found a way, hopefully, of imposing @CompileStatic which might be of some use. It involves the use of an inner class combined with subclassing from an abstract class, and throwing an exception if an illegal attempt is made to bypass the factory method. It was quite a hack, in fact.

More to the point, on closer examination I found that even this didn't work.

The only way I've found so far is such an uber-hack I feel ashamed to even mention it: technique involving examination of the stack trace.

On checking back with Groovy In Action 2nd Ed. it's clear to me that in fact @CompileStatic is much more about enforcement of type checking than anything to do with information-hiding: it is covered in such a way that it comes across as a rather more powerful version of the annotation @TypeChecked.

In Python there is a convention, I assume still used, of giving fields which are meant to be "treated as private" a special naming convention, namely making the name start with double-underscore. But I've seen nothing along these lines in Groovy.

Bloch, in his excellent book Effective Java, gives lots of sound reasons why information-hiding is very important. Of course it is well known by now that even in Java all this is flimsy and can be cracked by reflection techniques. But I'd like to hear from a Groovy uber-geek their view on the issue raised by your question...

like image 25
mike rodent Avatar answered Oct 27 '22 18:10

mike rodent