For example, instance of the following following class produces string A(x:7, values:[hello, world])
#ToString( includeNames=true )
class A {
def x
def values = []
}
How can I transform this String back to an instance of the class?
Based on the comments by #cfrick, this is not possible.
(Answering my own question for the sake of closing this topic.)
Related
I have been coding in Python for a bit of time now and very recently I got an ideas for some scripts that could break Python. One of those is assigning a value to the return value of a method. Why is this code legal? Shouldn't it cause an error? If no, are there any uses of such interpretation?
class SomeClass:
x = 10
def Method():
return SomeClass()
print(Method().x)
#This line concerns me!
Method().x = 5
print(Method().x)
The expression Method().x = 5 is legal, but basically does nothing. The code is first creating a new SomeClass instance, assigning 5 to its x field and finally discarding it as the SomeClass instance is not stored anywhere. The print(Method().x) afterwards is creating yet another fresh instance of SomeClass and printing the value of its x field which has been innitialized to 10 and never changed.
In short: every Method() is creating a new independent instance of the SomeClass class with their own separate x field.
This would work:
instance = Method()
instance.x = 5
print(instance.x)
Basically, you're creating a class called SomeClass with a class attribute x = 10.
As stated here, the class attribute is similar—but not identical—to the static keyword used in classes in some programming languages like C++ or Java.
According to python docs (https://docs.python.org/2/tutorial/classes.html), [...] Class attributes can also be assigned to, so you can change the value of SomeClass.x by assignment. [...]
However, this is not your case, cause everytime you invoke the Method() function you're basically creating a new instance of SomeClass().
EDIT: Look at this
class SomeClass:
x = 10
def Method():
return SomeClass()
myInstance = Method()
print(myInstance.x) # prints '10'
myInstance.x = 5
print(myInstance.x) # prints '5'
In this case, you're modifying the class attribute within the same instance of the class, called myInstance, and successfully changing its value as stated in the docs.
When I'm trying to use snakeyaml to dump out Yaml out of Groovy interpolated strings, it ends up printing a class name instead.
For example:
#Grab(group='org.yaml', module='snakeyaml', version='1.16')
import org.yaml.snakeyaml.Yaml
Yaml yaml = new Yaml();
def a = "a"
def list = ["$a"]
def s = yaml.dump(list)
Prints:
- !!org.codehaus.groovy.runtime.GStringImpl
metaClass: !!groovy.lang.MetaClassImpl {}
I'm guessing it has something to do with the fact that GStrings get transformed to Strings when they used and I suspect snakeyaml uses some sort of introspection to determine the class of the object.
Is there a better solution than calling toString() on all GStrings?
Try to create a new Representer :
public class GroovyRepresenter extends Representer {
public GroovyRepresenter() {
this.representers.put(GString.class, new StringRepresenter());
}
}
Yaml yaml = new Yaml(new GroovyRepresenter())
...
You could add type info to your variables
Yaml yaml = new Yaml();
def a = "a"
String aStr = "$a"
def list = [aStr]
def s = yaml.dump(list)
class GreetingPluginExtension {
def String message = 'Hello from GreetingPlugin'
}
Why is used def and String at the same time, we have String type, why we use and def keyword?
The comments already answered this question a long time ago, but here's an updated link to the relevant section of the Apache Groovy Style Guide:
https://groovy-lang.org/style-guide.html#_def_and_type
It says:
I often see developers using both def and a type. But def is redundant here. So make a choice, either use def or a type.
In general, my personal preference is for the clean, simple look of:
def message = "Hello"
Is there a way to bind properties from one instance of a class to the properties of an instance of another class (the common fields between the two). See the example below:
class One {
String foo
String bar
}
class Two {
String foo
String bar
String baz
}
def one = new One(foo:'one-foo', bar:'one-bar')
def two = new Two()
two.properties = one.properties
assert "one-foo" == two.foo
assert "one-bar" == two.bar
assert !two.baz
The result is an error: Cannot set readonly property: properties for class: Two
I would choose InvokerHelper.setProperties as I suggesed here.
use(InvokerHelper) {
two.setProperties(one.properties)
}
The problem is that for every object, .properties includes two built-in Groovy-defined properties, these are the metaClass and class. What you want to do is only set the user-defined properties. You can easily do this using code such as that shown below:
class One {
String foo
String bar
}
class Two {
String foo
String bar
String baz
}
def one = new One(foo:'one-foo', bar:'one-bar')
// You'll probably want to define a helper method that does the following 3 lines for any Groovy object
def propsMap = one.properties
propsMap.remove('metaClass')
propsMap.remove('class')
def two = new Two(propsMap)
assert "one-foo" == two.foo
assert "one-bar" == two.bar
assert !two.baz
I know in Groovy you can invoke a method on a class/object using a string. For example:
Foo."get"(1)
/* or */
String meth = "get"
Foo."$meth"(1)
Is there a way to do this with the class? I have the name of the class as a string and would like to be able to dynamically invoke that class. For example, looking to do something like:
String clazz = "Foo"
"$clazz".get(1)
I think I'm missing something really obvious, just am not able to figure it out.
As suggested by Guillaume Laforge on Groovy ML,
("Foo" as Class).get(i)
would give the same result.
I've tested with this code:
def name = "java.lang.Integer"
def s = ("$name" as Class).parseInt("10")
println s
Try this:
def cl = Class.forName("org.package.Foo")
cl.get(1)
A little bit longer but should work.
If you want to create "switch"-like code for static methods, I suggest to instantiate the classes (even if they have only static methods) and save the instances in a map. You can then use
map[name].get(1)
to select one of them.
[EDIT] "$name" is a GString and as such a valid statement. "$name".foo() means "call the method foo() of the class GString.
[EDIT2] When using a web container (like Grails), you have to specify the classloader. There are two options:
Class.forName("com.acme.MyClass", true, Thread.currentThread().contextClassLoader)
or
Class.forName("com.acme.MyClass", true, getClass().classLoader)
The first option will work only in a web context, the second approach also works for unit tests. It depends on the fact that you can usually use the same classloader as the class which invokes forName().
If you have problems, then use the first option and set the contextClassLoader in your unit test:
def orig = Thread.currentThread().contextClassLoader
try {
Thread.currentThread().contextClassLoader = getClass().classLoader
... test ...
} finally {
Thread.currentThread().contextClassLoader = orig
}
An augmentation to Chanwit's answer illustrating creation of an instance:
def dateClass = 'java.util.Date' as Class
def date = dateClass.newInstance()
println date
Here's another way
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def target = application.domainClasses.find{it.name == 'ClassName'}
target.clazz.invokeMethod("Method",args)
With this you don't need to specify the package name. Be careful though if you have the same class name in two different packages.
Melix on Groovy ML pointed me in the "right" direction on dynamic class method invokation awhile back, quite useful:
// define in script (not object) scope
def loader = this.getClass().getClassLoader()
// place this in some MetaUtils class, invoked on app startup
String.metaClass.toClass = {
def classPath = getPath(delegate) // your method logic to determine 'path.to.class'
Class.forName(classPath, true, this.loader)
}
// then, anywhere in your app
"Foo".toClass().bar()
You could create another string metaClass method to create instances as well, refactoring as appropriate:
String.metaClass.toObject = {
def classPath = getPath(delegate)
Class.forName(classPath, true, this.loader).newInstance()
}
Groovy is pure fun ;--)
I'm running version 1.8.8 groovy... and the simple example works.
Import my.Foo
def myFx="myMethodToCall"
def myArg = 12
Foo."$myFx"(myArg)
Calls Foo.myMethodToCall(12) as expected and desired. I don't know if this has always been the case though.