Is there a way to access global variable, declared in the script, from the static method of the class, declared in the same script?
For example
def s = "12345"
class MyClass {
static def method() {
println s
}
}
Because that way it fails with the error
You attempted to reference a variable in the binding or an instance variable from a static context
which is expected though.
There is a related discussion at this question:
Groovy scope - how to access script variable in a method
Related in that both questions refer to using a global variable within a class, but this question differs somewhat in that you are seeking to use a static method which alters how you pass the script instance or binding (2 choices).
Passing the Script's Instance
import groovy.transform.Field
#Field def s = "12345"
class MyClass {
static def method(si) {
return si.s
}
}
assert MyClass.method(this) == "12345"
Passing the Script's binding
s = "12345" // NOTE: no 'def'
class MyClass {
static def method(b) {
return b.s
}
}
assert MyClass.method(binding) == "12345"
Well, the problem is that in Groovy there is no such thing as a global variable. What is loosely considered a global variable is actually a static property within some class.
For example, if you remove the println line so that the code compiles, you get something like this out of the compiler:
public class script1455567284805 extends groovy.lang.Script {
...
public java.lang.Object run() {
return java.lang.Object s = '12345'
}
...
}
public class MyClass implements groovy.lang.GroovyObject extends java.lang.Object {
...
public static java.lang.Object method() {
// This is where the println would have been.
return null
}
...
}
As you can see, an additional class is created and the the s variable is declared within the method run() of that class. This makes the variable inaccessible to your other class.
Note: Removing the def will not address this issue.
Solution
Your best bet is to place your "global variables" into a class, possibly as static properties, like this:
class Global {
static Object S = "12345"
}
class MyClass {
static def method() {
println Global.S
}
}
You included a variable type with the s variable (by using the def type). In a Groovy script, this is treated as a local variable - and local to the run() method that is generated - which is kind of like a main() class. As a result, the variable is not available in other methods or classes.
If you remove the def you will be able to make use of the s variable.
Here is the Groovy documentation that explains this further.
Related
I have two immutable groovy classes that have a few shared values that I'm trying to abstract to a parent class. However when I create the following, the second test case always fails. Although everything compiles correctly and no error is thrown at runtime, when I assign the parent property int he constructor, it is never set, resulting in a null value. I havent found any documentation that forbids this, but I'm wondering is this even possible? I've tried a number of configuration of Annotations and class-types (e.g. removing abstract from the parent) but nothing seems to work short of just removing the #Immutable tag altogether.
abstract class TestParent {
String parentProperty1
}
#ToString(includeNames = true)
#Immutable
class TestChild extends TestParent {
String childProperty1
String childProperty2
}
class TestCase {
#Test
void TestOne() {
TestChild testChild = new TestChild(
childProperty1: "childOne",
childProperty2: "childTwo",
parentProperty1: "parentOne"
)
assert testChild
assert testChild.parentProperty1
}
}
Based on the code for the ImmutableASTTransformation, the Map-arg constructor added by the createConstructorMapCommon method does not include a call to super(args) in the method body.
which means that immutable classes are self contained by default
Now if you want to do it you need to use composition instead of inheritance and this is an example of how you can do it :
import groovy.transform.*
#TupleConstructor
class A {
String a
}
#Immutable(knownImmutableClasses=[A])
class B {
#Delegate A base
String b
}
def b = new B(base: new A("a"), b: "b")
assert b.a
i hope this will help :)
I've a simple class with a main() method from which I want to access the script bindings.
class Sample {
public static void main(String[] args) {
binding.variables.each{
println it.key
println it.value
}
}
}
Does the implicit binding variable exist for a simple class such as this or is it only for scripts?
I realize I can pass it into the method / the constructor of the object. But since I'd like to keep this as a pure class file and not a script, I cannot do that.
It's only a property of a Script instance. Look at the source, you'll see that a Binding is only a delegate, used in getProperty or setProperty.
I have the impression that closures run as the actual class being called (instead of the implementing super class) and thus break when some variables are not visible (e.g. private in the super class).
For example
package comp.ds.GenericTest2
import groovy.transform.CompileStatic
#CompileStatic
class ClosureScopeC {
private List<String> list = new ArrayList<String>()
private int accessThisPrivateVariable = 0;
void add(String a) {
list.add(a)
println("before ${accessThisPrivateVariable} ${this.class.name}")
// do something with a closure
list.each {String it ->
if (it == a) {
// accessThisPrivateVariable belongs to ClosureScopeC
accessThisPrivateVariable++
}
}
println("after ${accessThisPrivateVariable}")
}
}
// this works fine
a = new ClosureScopeC()
a.add("abc")
a.add("abc")
// child class
class ClosureScopeD extends ClosureScopeC {
void doSomething(String obj) {
this.add(obj)
}
}
b = new ClosureScopeD()
// THIS THROWS groovy.lang.MissingPropertyException: No such property: accessThisPrivateVariable for class: comp.ds.GenericTest2.ClosureScopeD
b.doSomething("abc")
The last line throws a MissingPropertyException: the child class calls the "add" method of the super class, which executes the "each" closure, which uses the "accessThisPrivateVariable".
I am new to groovy, so I think there must be an easy way to do this, because otherwise it seems that closures completely break the encapsulation of the private implementation done in the super class ... this seems to be a very common need (super class implementation referencing its own private variables)
I am using groovy 2.1.3
I found this to be a good reference describing how Groovy variable scopes work and applies to your situation: Closure in groovy cannot use private field when called from extending class
From the above link, I realized that since you have declared accessThisPrivateVariable as private, Groovy would not auto-generate a getter/setter for the variable. Remember, even in Java, private variables are not accessible directly by sub-classes.
Changing your code to explicitly add the getter/setters, solved the issue:
package com.test
import groovy.transform.CompileStatic
#CompileStatic
class ClosureScopeC {
private List<String> list = new ArrayList<String>()
private int accessThisPrivateVariable = 0;
int getAccessThisPrivateVariable() { accessThisPrivateVariable }
void setAccessThisPrivateVariable(int value ){this.accessThisPrivateVariable = value}
void add(String a) {
list.add(a)
println("before ${accessThisPrivateVariable} ${this.class.name}")
// do something with a closure
list.each {String it ->
if (it == a) {
// accessThisPrivateVariable belongs to ClosureScopeC
accessThisPrivateVariable++
}
}
println("after ${accessThisPrivateVariable}")
}
}
// this works fine
a = new ClosureScopeC()
a.add("abc")
a.add("abc")
// child class
class ClosureScopeD extends ClosureScopeC {
void doSomething(String obj) {
super.add(obj)
}
}
b = new ClosureScopeD()
b.doSomething("abc")
There is a simpler way, just make the access modifier (should rename the property really) to protected, so the sub-class has access to the property.. problem solved.
protected int accessThisProtectedVariable
To clarify on your statement of concern that Groovy possibly has broken encapsulation: rest assured it hasn't.
By declaring a field as private, Groovy is preserving encapsulation by intentionally suspending automatic generation of the public getter/setter. Once private, you are now responsible and in full control of how or if there is a way for sub-classes (protected) or all classes of objects (public) to gain access to the field by explicitly adding methods - if that makes sense.
Remember that by convention, Groovy ALWAYS calls a getter or setter when your codes references the field. So, a statement like:
def f = obj.someField
will actually invoke the obj.getSomeField() method.
Likewise:
obj.someField = 5
will invoke the obj.setSomeField(5) method.
I have a closure within an object Foo and inside the closure i define a method called 'myStaticMethod' that I want to resolve once the closure is called outside the object Foo. I also happen to have 'on purpose' a static method within my object Foo with the same name. When I call the closure i set the 'resolve strategy' to DELEGATE_ONLY to intercept the call to myStaticMethod that is defined within the closure.
I tried to achieve that through missingMethod but the method is never intercepted. When i make the Foo.myStaticMethod non static, the method is intercepted. I don't quite understand why this is happening though my resolve strategy is set to DELEGATE_ONLY. having the Foo.myStaticMethod static or not shouldn't matter or I am missing something
class Foo {
static myclosure = {
myStaticMethod()
}
static def myStaticMethod() {}
}
class FooTest {
def c = Foo.myclosure
c.resolveStrategy = Closure.DELEGATE_ONLY
c.call()
def missingMethod(String name, def args) {
println $name
}
}
To solve the problem, I ended up overriding the invokeMethod right before calling the closure in FooTests
Foo.metaClass.'static'.invokeMethod = { String name, args ->
println "Static Builder processing $name "
}
While trying to solve this problem, i discovered a very weird way to intercept missing static methods. Might be useful to some of you in the future.
static $static_methodMissing(String name, args) {
println "Missing static $name"
}
-Ken
Static methods unfortunately aren't intercepted by the closure property resolution. The only way that I know to intercept those is to override the static metaClass invokeMethod on the class that owns the closure, ex:
class Foo {
static myclosure = {
myStaticMethod()
}
static myStaticMethod() {
return false
}
}
Foo.metaClass.'static'.invokeMethod = { String name, args ->
println "in static invokeMethod for $name"
return true
}
def closure = Foo.myclosure
assert true == closure()
I've developed a Class that has some methods that augment Integer, it mainly lets me do this:
def total = 100.dollars + 50.euros
Now I have to extend Integer.metaClass doing something like this:
Integer.metaClass.getDollars = {->
Money.Dollar(delegate)
}
I tried putting that at the bottom of the file, before the Money class declaration, but the compiler says that a class Named Money already exists, I know why it happens (because groovy creates a class with the name of the file with an empty static void main to run this code).
I also tried using a static block inside the class like this:
static {
Integer.metaClass.getDollars = {->
Money.Dollar(delegate)
}
}
This neither works.
A third solution would be to change the file name (like MoneyClass.groovy) and keep the class name (class Money) but that seems a bit weird.
Is there anything else I can do? Thanks.
Just put it in any method of any class maybe a bean TypeEnhancer.groovy:
public class TypeEnhancer {
public void start() {
Integer.metaClass.getDollars() = {-> Money.Dollar(delegate) }
}
public void stop() {
Integer.metaClass = null
}
}
Just create and initalize by calling start(): new TypeEnhancer().start();.
To disable the enhancement, call new TypeEnhancer().stop();. The bean can also used as Spring bean.