"def Type" declarations in groovy classes - groovy

Suddenly I've realized that I can write
class Person {
def String name
}
My question is - what is the difference between code provided above and classic:
class Person {
String name
}
Why first form even exists?

There's no difference. Adding def to the beginning of a type definition does nothing. However, it is allowed by the parser.
One way to check stuff like this, is to fire up the groovyConsole, and run the AST Browser (which for the script)
class Person {
def String name
}
Shows:
public class Person extends java.lang.Object {
private java.lang.String name
}

Related

Error Caught: groovy.lang.GroovyRuntimeException: This script or class could not be run

Recently encountered with this problem- when i had two class in one groovy class if main method class is not on the top i encountered this problem.
class Book {
private String name
void setName(String bookName) {
name=bookName
print "Book Name => "+bookName+"\n";
}
String getName(){
return name;
}
}
class TestClass {
static main(args) {
Book t = new Book();
t.setName("First Book");
print "book title=>"+t.getName()+"\n"
}
}
But if change the order of these two class than there is no error, does it mean main method class should be on top in Groovy ?
Yes, order of classes matter in a groovy script. If you parse a groovy script and check its class name, it would the top level class and not the one with main method or one that have same name as the name of the file. It could be a concrete class, an abstract class, an enum, an interface or trait.
Lets see your case. We are going to put your code inside a GString and then will try to parse it using our own GroovyClassLoader.
String script = """
class Book {
private String name
void setName(String bookName) {
name=bookName
print "Book Name => "+bookName+"\\n";
}
String getName(){
return name;
}
}
class TestClass {
static main(args) {
Book t = new Book();
t.setName("First Book");
print "book title=>"+t.getName()+"\\n"
}
}
"""
GroovyClassLoader loader = new GroovyClassLoader()
GroovyCodeSource codeSource = new GroovyCodeSource(script, "MyClass", GroovyShell.DEFAULT_CODE_BASE)
println loader.parseClass(codeSource)
When you will execute this code it will print class Book. Because this is the first available class in your script.
The exception you are getting is because that your top level class doesn't have a main method and neither your script have something to execute after you have loaded your classes. One solution is to move TestClass at top or just add another line at the end of the file TestClass.main() and it will execute without any issues.

groovy immutable object with parent class

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 :)

Groovy - Is it possible to pass a class as a parameter?

This is just a very basic example of what I want to do. There's a bit more that goes on in the foobar method, but it's the gist of what I'm doing. It obviously doesn't work, since it fails to compile, but I'm wondering if I'm just passing the class incorrectly or using the 'className' parameter in the wrong way. I know I can rework it to take the string of the class name and just match it, but it seems a shame to do that. This would be so nice and DRY.
class Foo {
String name
}
class Bar {
String name
}
def foobar(field, className) {
def instance = className.findByName(jsonParams.field)
if(!instance) {
instance = new className(name: jsonParams.field)
}
return instance
}
foobar(foos, Foo)
foobar(bars, Bar)
I don't know much Java or Groovy, so I'm not sure what's possible vs impossible yet. Feel free to just tell me "No." I've tried googling and haven't found anything that really answers the question for me. A simple no would be great at this point haha.
Yes, it is possible to pass class as argument - in Groovy, classes are first class citizens (see this thread for more detail).
This construct: instance = new className(name: jsonParams.field) actually tries to create an instance of class named className, not of the class referenced by this variable. To make it compile, you need to call Class.newInstance:
class Foo {
String name
}
class Bar {
String name
}
def foobar(String name,Class clazz) {
def instance = clazz.findByName(name)
if(!instance) {
instance = clazz.newInstance(name:name)
}
return instance
}
foobar('foo', Foo)
foobar('bar', Bar)
​
I'm not entirely sure what you want to achieve with the findByName method, though - neither Foo nor Bar have a static method named findByName as far as I can tell.

Add a missing property in a groovy #Canonical bean constructor call?

I am new to groovy and just started exploring its metaprogramming capabilities. I got stuck with adding missing properties on a bean constructor call.
In a class to be used with FactoryBuilderSupport, I want to dynamically add those properties that are not yet defined and provided during the constructor call. Here is stripped-down version:
#Canonical
class MyClass {
def startDate
def additionalProperties = [:]
def void propertyMissing(String name, value) {
additionalProperties[name] = value
}
}
However, If I construct the class with unknown properties, the proprty is not added but I get a MissingPropertyException instead:
def thing = new MyClass(startDate: DateTime.now(), duration: 1234)
The property duration does not exist, and I expected it to be handled via propertyMissing. As far as I understand groovy, calling the tuple-constructor results in a no-argument constructor call followed by calls to the groovy-generated setters. So why do I get a MissingPropertyException?
As I am new to groovy, I am probably missing some basic AST or MOP rules. I would highly appreciate your help.
If you use #Canonical and you define the first class object with def like you are doing with startDate the annotation generates the following constructors:
#Canonical
class MyClass {
def startDate
def additionalProperties = [:]
def void propertyMissing(String name, value) {
additionalProperties[name] = value
}
}
// use reflection to see the constructors
MyClass.class.getConstructors()
Generated constructors:
public MyClass()
public MyClass(java.lang.Object)
public MyClass(java.util.LinkedHashMap)
public MyClass(java.lang.Object,java.lang.Object)
In the #Canonical documentation you can see the follow limitation:
Groovy's normal map-style naming conventions will not be available if the first property has type LinkedHashMap or if there is a single Map, AbstractMap or HashMap property
Due to public MyClass(java.util.LinkedHashMap) is generated you can't use tuple-constructor and you get MissingPropertyException.
Surprisingly if you define your first object (note that I say the first) with a type instead of using def, #Canonical annotation doesn't add the public MyClass(java.util.LinkedHashMap) and then your tuple-constructor call works, see the following code:
#Canonical
class MyClass {
java.util.Date startDate
def additionalProperties = [:]
def void propertyMissing(String name, value) {
additionalProperties[name] = value
}
}
// get the constructors
MyClass.class.getConstructors()
// now your code works
def thing = new MyClass(startDate: new java.util.Date(), duration: 1234)
Now the created constructors are:
public MyClass()
public MyClass(java.util.Date)
public MyClass(java.util.Date,java.lang.Object)
So since there isn't the public MyClass(java.util.LinkedHashMap) the limitation doesn't apply and you tuple-constructor call works.
In addition I want to say that since this solution works I can't argue why... I read the #Canonical documentation again and again and I don't see the part where this behavior is described, so I don't know why works this way, also I make some tries and I'm a bit confusing, only when the first element is def the public MyClass(java.util.LinkedHashMap) is created i.e:
#Canonical
class MyClass {
def a
int c
}
// get the constructors
MyClass.class.getConstructors()
First object defined as def...
public MyClass()
public MyClass(java.lang.Object)
public MyClass(java.util.LinkedHashMap) // first def...
public MyClass(java.lang.Object,int)
Now if I change the order:
#Canonical
class MyClass {
int c
def a
}
// get the constructors
MyClass.class.getConstructors()
Now first is not def and public MyClass(java.util.LinkedHashMap) is not generated:
public MyClass()
public MyClass(int)
public MyClass(int,java.lang.Object)
Hope this helps,

Groovy expando metaclass

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.

Resources