Why just annotating with #CompileStatic makes the below code to give NullPointerException?
class GroovyEach {
static def main(args) {
List items = null
items.each {
println 'hello'
}
}
}
Below code gives exception.
import groovy.transform.CompileStatic
#CompileStatic
class GroovyEach {
static def main(args) {
List items = null
items.each {
println 'hello'
}
}
}
Stacktrace:
Exception in thread "main" java.lang.NullPointerException
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1372)
at trial.GroovyEach.main(GroovyEach.groovy:10)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
This is the inverse of an older question. When compiled statically, items is of type List, when not compiled statically the type is NullObject, which retrieves the iterator in a null-safe fashion. This is trivial to demonstrate.
This works
class GroovyEach {
static void main(String[] args) {
List items = null
(org.codehaus.groovy.runtime.NullObject) items
}
}
this fails with [Static type checking] - Inconvertible types: cannot cast java.util.List to org.codehaus.groovy.runtime.NullObject
#groovy.transform.CompileStatic
class GroovyEach {
static void main(String[] args) {
List items = null
(org.codehaus.groovy.runtime.NullObject) items
}
}
Related
I have the following code. I have an abstract JobParams, a class extending that abstract GradleJobParams, and a gjp variable with value using anonymous class declaration.
I want to test the overriding behavior of groovy. I can override the method setupRoot() but not the property testVar, why is that?
Tested on: https://groovyconsole.appspot.com/script/5146436232544256
abstract class JobParams {
int root
def testVar=1
def setupRoot () {
println("The root");
}
def printTestVar () {
println("The testVar:" + testVar);
}
}
class GradleJobParams extends JobParams {
}
def gjp = [
testVar:3,
setupRoot:{
println("Override root");
}
] as GradleJobParams;
println("Starting");
gjp.printTestVar();
gjp.setupRoot();
The result is:
Starting
The testVar:1
Override root
Java (and thus Groovy) does not support overriding fields from the parent class with subclassing. Instead, it uses a mechanism called hiding fields:
Hiding Fields
Within a class, a field that has the same name as a field in the superclass hides the superclass's field, even if their types are different. Within the subclass, the field in the superclass cannot be referenced by its simple name. Instead, the field must be accessed through super, which is covered in the next section. Generally speaking, we don't recommend hiding fields as it makes code difficult to read.
Source: https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html
It can be simply illustrated with the following example in Java:
final class SubclassHiddingFieldExample {
static abstract class A {
int value = 10;
void printValue1() {
System.out.println(value);
}
void printValue2() {
System.out.println(this.value);
}
void printValue3() {
System.out.println(((B)this).value);
}
}
static class B extends A {
int value = 12;
}
public static void main(String[] args) {
final B b = new B();
b.printValue1();
b.printValue2();
b.printValue3();
}
}
Output:
10
10
12
As you can see, only printValue3 prints out 3, because it cast this explicitly to B class.
Now, if you look at the decompiled bytecode of your JobParams class, you can see that the printTestVar method code is an equivalent of the following Java code:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.callsite.CallSite;
public abstract class JobParams implements GroovyObject {
private int root;
private Object testVar;
public JobParams() {
CallSite[] var1 = $getCallSiteArray();
byte var2 = 1;
this.testVar = Integer.valueOf(var2);
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
}
public Object setupRoot() {
CallSite[] var1 = $getCallSiteArray();
return var1[0].callCurrent(this, "The root");
}
public Object printTestVar() {
CallSite[] var1 = $getCallSiteArray();
return var1[1].callCurrent(this, var1[2].call("The testVar:", this.testVar));
}
public MetaClass getMetaClass() {
MetaClass var10000 = this.metaClass;
if (var10000 != null) {
return var10000;
} else {
this.metaClass = this.$getStaticMetaClass();
return this.metaClass;
}
}
public void setMetaClass(MetaClass var1) {
this.metaClass = var1;
}
public Object invokeMethod(String var1, Object var2) {
return this.getMetaClass().invokeMethod(this, var1, var2);
}
public Object getProperty(String var1) {
return this.getMetaClass().getProperty(this, var1);
}
public void setProperty(String var1, Object var2) {
this.getMetaClass().setProperty(this, var1, var2);
}
public int getRoot() {
return this.root;
}
public void setRoot(int var1) {
this.root = var1;
}
public Object getTestVar() {
return this.testVar;
}
public void setTestVar(Object var1) {
this.testVar = var1;
}
}
You can see that the line that prints out the value of the testVar field is represented by:
return var1[1].callCurrent(this, var1[2].call("The testVar:", this.testVar));
It means that no matter what value of testVar your subclass defines, the printTestVar method uses testVar field defined in the JobParams class. Period.
Using Groovy auto getter methods
There is one way you to implement the expected behavior. Every class field in Groovy has a getter method associated with that field compiled by Groovy for you. It means that you can access testVar by calling the getTestVar() method generated by the Groovy compiler. You can use it to override the value returned by a getter method for any field from the subclass. Consider the following example:
abstract class JobParams {
int root
def testVar=1
def setupRoot () {
println("The root");
}
def printTestVar () {
println("The testVar:" + getTestVar()); // <-- using a getTestVar() method instead a testVar field
}
}
class GradleJobParams extends JobParams {
}
def gjp = [
getTestVar: 3, // <-- stubbing getTestVar() method to return a different value
setupRoot:{
println("Override root");
}
] as GradleJobParams;
println("Starting");
gjp.printTestVar();
gjp.setupRoot();
Output:
Starting
The testVar:3
Override root
Why does the code below give GroovyCastException?
class Main {
public static void main(String... args) {
Test test = new Test()
}
}
#CompileStatic #Newify
class Test {
private HashMap<String, A> hashMap = [:]
public Test() {
set()
test()
}
public void test() {
hashMap.each() { String string, A a ->
a.printString()
}
}
public void set() {
hashMap.put("aaa", B.new("xxx"))
hashMap.put("bbb", B.new("yyy"))
}
}
class A {
public String string = ""
public void printString() {
println(string)
}
}
class B extends A {
public B(String string) {
this.string = string
}
}
The stacktrace is:
Exception in thread "main" org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'main.Test#543e710e' with class 'main.Test' to class 'main.A'
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnSAM(DefaultTypeTransformation.java:405)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnNumber(DefaultTypeTransformation.java:319)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToType(DefaultTypeTransformation.java:232)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.castToType(ScriptBytecodeAdapter.java:603)
at main.Test$_test_closure1.doCall(Main.groovy:37)
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:483)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1019)
at groovy.lang.Closure.call(Closure.java:426)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.callClosureForMapEntry(DefaultGroovyMethods.java:5226)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2107)
at main.Test.test(Main.groovy:36)
at main.Test.<init>(Main.groovy:32)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:80)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:105)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:60)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:235)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:239)
at main.Main.main(Main.groovy:19)
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:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
But when #CompileStatic is omitted, or when #Newify is omitted and B.new is replaced by new B, the code above gives no exception and runs as I expected.
The version of Groovy is 2.4.5.
It's probably a bug in groovy. You should maybe open an issue on JIRA.
If I decompile Test.test closure, I see :
public Object doCall(String string, A a) {
((A)ScriptBytecodeAdapter.castToType(((_test_closure1)this).getThisObject(), A.class))
.printString();
return null;
}
Groovy try to convert 'this' to A, not the variable a.
Without #Newify, the generation is correct :
public Object doCall(String k, A a) {
a.printString();
return null;
}
I'm looking to extend an inner class in Groovy, it's part of a mix-in pattern that needs a groovy calculation method to be added after class construction but natively use the accessors of the main class. Works in Java, not so much in Groovy. Help appreciated.
p.s. realize that with { } groovy construct may be an alternative... trying to get this to work
Base class (in Java):
public class JavaClass {
private String foo = "Foo";
private Calculation calculation;
public JavaClass() {
calculation = new Calculation();
}
public JavaClass(Calculation calculation) {
this.calculation = calculation;
}
public String getFoo() {
return foo;
}
public String getBar() {
return calculation.getBar();
}
public class Calculation {
public String getBar() {
return null;
}
}
}
I create a Groovy implementation: (I know it's a bit hokey, bear with me)
class GroovyCalculationClass extends JavaClass {
public GroovyCalculationClass() { }
public Calculation getCalculation() {
return new Calculation();
}
public class Calculation extends JavaClass.Calculation {
public String getBar() {
return getFoo() + "Bar";
}
}
}
Run this test:
class TestCalculation {
#Test
public void testCalculation() {
JavaClass javaClass = new JavaClass(
new GroovyCalculationClass().getCalculation());
Assert.assertEquals("FooBar", javaClass.getBar());
}
}
And get this:
java.lang.ArrayIndexOutOfBoundsException: 0
at com.modelshop.datacore.generator.GroovyCalculationClass$Calculation.(GroovyCalculationClass.groovy)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:57)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
at com.modelshop.datacore.generator.GroovyCalculationClass.getCalculation(GroovyCalculationClass.groovy:12)
at com.modelshop.datacore.generator.GroovyCalculationClass$getCalculation.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
at com.modelshop.datacore.generator.TestCalculation.testCalculation(TestCalculation.groovy:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Replace GroovyCalculationClass with identical JavaCalculationClass and everything works.
My app has the following directory structure:
myapp/
src/
com.me.myapp/
Driver.groovy
Fizz.groovy
stages (package)
AbstractStage.groovy
impl (package)
DefaultStage.groovy
Where:
class Driver {
static void main(String[] args) {
AbstractStage stage1 = new DefaultStage()
stage1.derp
}
}
abstract class AbstractStage {
Set<Fizz> fizzes = new HashSet<Fizz>()
void derp() {
println "I have " + fizzes.size + " attached fizzes!"
}
abstract void doSomething()
}
class DefaultStage extends AbstractStage {
Set<Fizz> executedFizzes = new HashSet<Fizz>()
#Override
void doSomething() {
// TODO: Implement
}
}
When I run this I get:
Exception in thread "main" groovy.lang.MissingPropertyException: No such property: derp for class:com.me.myapp.stages.impl.DefaultStage
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:50)
at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:49)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:231)
at com.me.myapp.Driver.main(Driver.groovy:6)
...where Driver.groovy:6 corresponds to the line that calls derp.
What's going on here?
derp is a method, not a property, so you need to invoke it:
static void main(String[] args) {
AbstractStage stage1 = new DefaultStage()
stage1.derp() // You need the '()' here.
}
This is my XML file
<BADFM>
<Given>
<Ord>
<Bag IDC="DM" />
</Ord>
</Given>
</BADFM>
This is my Parser class
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
public class Test {
public static void main(String args[]) throws Exception {
File file = new File("D:\\BADML.xml");
JAXBContext jaxbContext = JAXBContext
.newInstance(MyMessage.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
MyMessage authentifyResult = (MyMessage) jaxbUnmarshaller
.unmarshal(file);
System.out.println(authentifyResult.getGiven().getOrd().getBag().getIDC());
}
}
This is MyMessage
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="BADFM")
public class MyMessage
{
#XmlElement(name="Given")
private Given given;
public Given getGiven() {
return given;
}
public void setGiven(Given given) {
this.given = given;
}
}
This is Given.java
import javax.xml.bind.annotation.XmlElement;
public class Given {
private Ord ord;
#XmlElement(name = "Ord")
public Ord getOrd() {
return ord;
}
public void setOrd(Ord ord) {
this.ord = ord;
}
}
This is Ord.java
import javax.xml.bind.annotation.XmlElement;
public class Ord {
private Bag bag;
#XmlElement(name="Bag")
public Bag getBag() {
return bag;
}
public void setBag(Bag bag) {
this.bag = bag;
}
}
This is Bag.java
import javax.xml.bind.annotation.XmlAttribute;
public class Bag {
#XmlAttribute(name="IDC")
private String IDC ;
public String getIDC() {
return IDC;
}
#XmlAttribute(name="IDC")
public void setIDC(String IDC) {
IDC = IDC;
}
}
When i ran that i am getting
Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "IDC"
this problem is related to the following location:
at public java.lang.String Bag.getIDC()
at Bag
at public Bag Ord.getBag()
at Ord
at public Ord Given.getOrd()
at Given
at public Given MyMessage.getGiven()
at MyMessage
this problem is related to the following location:
at private java.lang.String Bag.IDC
at Bag
at public Bag Ord.getBag()
at Ord
at public Ord Given.getOrd()
at Given
at public Given MyMessage.getGiven()
at MyMessage
at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(Unknown Source)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(Unknown Source)
at com.sun.xml.internal.bind.v2.ContextFactory.createContext(Unknown Source)
at com.sun.xml.internal.bind.v2.ContextFactory.createContext(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at javax.xml.bind.ContextFinder.newInstance(Unknown Source)
at javax.xml.bind.ContextFinder.find(Unknown Source)
at javax.xml.bind.JAXBContext.newInstance(Unknown Source)
at javax.xml.bind.JAXBContext.newInstance(Unknown Source)
at Test.main(Test.java:11)
You need to use the #XmlAttribute for the IDC variable of the Bag class. Once you make that change, the XML you referenced at the top will work.
As for your current code, it is expecting the XML to look like:
<Bag>
<IDC>DM</IDC>
</Bag>
You can easily see what type of XML your classes are expecting by populating all attribute fields and then marshaling the object to a file.
Update
You should always declare your attributes as private if you are going to have properly named getters and setters. Otherwise, JAXB will throw the error Class has two properties of the same name.
When declaring that a class attribute is a #XmlAttribute, you should also put the annotation on the getter so that JAXB does not think you want both a #XmlAttribute and #XmlElement with the same name.
public class Bag {
private String IDC ;
#XmlAttribute(name="IDC")
public String getIDC() {
return IDC;
}
public void setIDC(String IDC) {
this.IDC = IDC;
}
}