Information-hiding in Groovy (using closures? nami

2019-08-26 23:48发布

问题:

This follows from my unsuccessful attempt to find an answer to this question from 2014.

It's not clear to me whether there might in fact be some techniques in Groovy to use closures, specifically, to hide information. All I can say is that if information on such techniques is out there it is a perfect illustration, precisely, of "information-hiding": I cannot find it!

But failing that I think my understanding now is that absolutely zero attempt to hide information (or pretend to, as in Java - bearing in mind reflection techniques) is ever made. This appears to be by design, but also due to the requirements of Groovy's dynamic nature. It seems, for example, that @CompileStatic, mentioned in the referenced question, is more about type-checking than anything else.

But in Python, for example, there is a convention (I assume still used) to make "fields which are meant to be considered private" begin with a double underscore. I've never heard anyone talking about this in connection with Groovy.

Aren't information-hiding and encapsulation, or at least conventions to encourage disciplined use of the "intimate state" of objects, good things? Any Groovy experts care to comment?

later

daggett has given an answer which is interesting in some ways, but not really what I had in mind. Consider this:

class Main {
    static main( args ) {
        def sm = new SecurityManager()
        System.setSecurityManager( sm )
        println new Bob().doSomethingProtected()
    }
}


class Bob {
    public doSomethingPublic() {
        "public"
    }
    private doSomethingPrivate() {
        "private"
    }
    protected doSomethingProtected() {
        "protected"
    }
}

... whichever one of these Bob methods is called it will pass with the SecurityManager not set, but fail with it set. It also doesn't matter which package it's in. Nor does it matter whether Bob is in a subpackage (for example), with @PackageScope: it is only if Main.main is given @CompileStatic that this will help (see referenced question). I'm also not clear about precisely what you can do with a SecurityManager set in this way: is it possible to enforce private or protected (or package-private) in some way? At the moment I just don't know and will have to investigate.

As for the other suggestion, it's intriguing, but doesn't in fact deny "visibility" as suggested. You'd also need to include the following method in class A:

def getI() {
    throw new Exception()
}

After that, yes, visibility is denied to every other class, whether in the same package or not, and also these "private" elements are not even visible to other objects of the same class (! - unlike Java). In that sense it does indeed deliver a very Draconian privacy. But to me it's also a bit of a hack. I'm not quite clear about this GroovyObjectSupport class or what it does, and will have to investigate it. Finally, there is little point in actually giving these fields the private modifier. As I said, the ONLY function of private in Groovy is to deny visibility of these fields to subclasses of daggett's class A here.

Having only a stark choice between a super-Draconian and hackish "private", or "unrestrictedly public", clearly represents a considerable "impoverishment" of choice of visibility compared to Java, where you have not only protected but also package-private (subject, yes yes yes, of course, to use of reflection...), and where private fields are visible to other objects of the same class.

回答1:

SecurityManager

don't run following code from GroovyConsole. only from groovy command line.

def sm = new SecurityManager()
System.setSecurityManager(sm)
//without previous lines the following code will run successfully
println new ByteArrayOutputStream().buf

this will throw the following exception

Caught: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessDeclaredMembers")
java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:109)
        at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:131)
Caused by: java.lang.ExceptionInInitializerError
        at groovy.ui.GroovyMain.run(GroovyMain.java:397)
        at groovy.ui.GroovyMain.process(GroovyMain.java:370)
        at groovy.ui.GroovyMain.processArgs(GroovyMain.java:129)
        at groovy.ui.GroovyMain.main(GroovyMain.java:109)
        ... 6 more
Caused by: java.security.AccessControlException: access denied ("java.util.logging.LoggingPermission" "control")
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
        at java.security.AccessController.checkPermission(AccessController.java:884)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at java.util.logging.LogManager.checkPermission(LogManager.java:1586)
        at java.util.logging.Logger.checkPermission(Logger.java:422)
        at java.util.logging.Logger.setUseParentHandlers(Logger.java:1799)
        at org.codehaus.groovy.runtime.StackTraceUtils.<clinit>(StackTraceUtils.java:57)
        ... 10 more

control access with getProperty & setProperty

class A extends GroovyObjectSupport{
    private int i=555
    private int j=666
    def f(){
        println "i=$i j=$j"
    }
    Object getProperty(String name){
        if(name in ['i'])throw new Exception("Access to property `$name` is denied")
        return super.getProperty(name)
    }
}
def a=new A()
a.f()
println "a.j = ${a.j}"
println "a.i = ${a.i}"

this will allow access to member j but not to the member i outside of class.

output:

i=555 j=666
a.j = 666
Exception thrown

java.lang.Exception: Access to property `i` is denied
  ...