groovy protected property can't be overriden in subclass - groovy

subclass call parent protected method which expect return a protected override property.
but return the parent's property.
//ParentClass:
package tz
import java.util.List;
class AbstractController {
protected List keywordFilter = []
protected String methodKey(){
return "\t[method]parent,$keywordFilter,"+keywordFilter.toString()
}
def closureKey(){
return "\t[closure]parent,$keywordFilter,"+keywordFilter.toString()
}
}
//SubClass:
package tz
import java.util.List;
class SubController extends AbstractController{
protected List keywordFilter = ['a']
public SubController(){
}
public void test(){
println "subCall:"+methodKey()+closureKey()
}
def test2 = {
println "c,$keywordFilter,"+methodKey()+closureKey()
}
public static void main(String[] args) {
def s = new SubController()
s.test()
s.test2()
}
}
//Output:
subCall:[method]parent,[],[] [closure]parent,[],[]
c,[a], [method]parent,[],[] [closure]parent,[],[]

In Java and Groovy, fields are not overriden in subclasses. The base class version is just hidden by the subclass version. You actually get two fields in the class, with the same name. The base class methods will see the base class field and subclass methods will see the subclass field.
The solution is usually to just wrap the field in a getter method. In groovy:
class AbstractController {
protected List getKeywordFilter() { [] }
...
}
class SubController extends AbstractController {
protected List getKeywordFilter() { ['a'] }
...
}
Following the groovy property conventions, you can still reference it as "$keywordFilter" which will automatically call the getter.

Related

Could not find matching constructor for anonymous class

Consider the following code sample
class A {
int data
}
class B extends A {}
def o1 = new B(data: 1)
// This works correctly.
def o2 = new A(data:1) {}
// This will throw the following error
// Exception thrown
//
// groovy.lang.GroovyRuntimeException: Could not find matching constructor for: A(LinkedHashMap)
// at ConsoleScript2$1.<init>(ConsoleScript2)
// at ConsoleScript2.run(ConsoleScript2:11)
// at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
// at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
// at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
To me, the anonymous one should be the same as the named class. But it turns out that Groovy treats them differently. I want to know how to fix it. Thank you.
You see this error because of the nature of the dynamic map constructor - it is not added explicitly to the generated classes, but it is called through the CallSite.callConstructor(obj,map) method instead. However, there is a solution to that problem.
Consider the following exemplary test.groovy script:
class A {
int data
}
class B extends A {}
def a1 = new B(data: 1)
def a2 = new A(data: 2) {}
println a1
println a2
When you decompile generated A.class file, you will something like this:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.transform.Generated;
import groovy.transform.Internal;
import java.beans.Transient;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class A implements GroovyObject {
private int data;
#Generated
public A() {
CallSite[] var1 = $getCallSiteArray();
super();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
#Generated
#Internal
#Transient
public MetaClass getMetaClass() {
MetaClass var10000 = this.metaClass;
if (var10000 != null) {
return var10000;
} else {
this.metaClass = this.$getStaticMetaClass();
return this.metaClass;
}
}
#Generated
#Internal
public void setMetaClass(MetaClass var1) {
this.metaClass = var1;
}
#Generated
public int getData() {
return this.data;
}
#Generated
public void setData(int var1) {
this.data = var1;
}
}
This class has only one no-args constructor. When you decompile the test.class file (compiled Groovy script file), you will see something like this:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class test extends Script {
public test() {
CallSite[] var1 = $getCallSiteArray();
super();
}
public test(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, test.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
Object a1 = var1[1].callConstructor(B.class, ScriptBytecodeAdapter.createMap(new Object[]{"data", 1}));
Object a2 = new test.1(ScriptBytecodeAdapter.createMap(new Object[]{"data", 2}));
var1[2].callCurrent(this, a1);
return var1[3].callCurrent(this, a2);
}
public class 1 extends A {
}
}
Take a look at how objects a1 and a2 are initialized. The a1 object is initialized in the following way:
Object a1 = var1[1].callConstructor(B.class, ScriptBytecodeAdapter.createMap(new Object[]{"data", 1}));
It uses the CallSite.callConstructor() method to mimic the map constructor which does not exist in the A class. If we look at how the object a2 is initialized we will find this:
Object a2 = new test.1(ScriptBytecodeAdapter.createMap(new Object[]{"data", 2}));
We can see that Groovy in the case of the anonymous class (which is not anonymous at all - Groovy generates a class anyway), Groovy uses a direct constructor call. And it fails, because there is no A(LinkedHashMap) constructor in the parent class.
Solution
Luckily, there is a solution to this problem - you can use #MapConstructor and #InheritConstructors annotations to force creating map constructor in the A class, and to inherit this constructor in the B class. Take a look at this working example:
import groovy.transform.InheritConstructors
import groovy.transform.MapConstructor
#MapConstructor
class A {
int data
}
#InheritConstructors
class B extends A {}
def a1 = new B(data: 1)
def a2 = new A(data: 2) {}
println a1
println a2
The only requirement is to use at least the Groovy 2.5 version which introduced the #MapConstructor annotation.

How to override Groovy variable and method using anonymous class?

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

JAXB dynamic XML root class name

I have my User class in java. When I want to unmarshal it, I get xml with <UserIn> root element, and when I want to marshal it I should do <UserOut> to be XML root element. If I provide #XmlRootElement("UserIn") it is not dynamic and it is always UserIn root. Is there any way to do dynamic root element on class? thanks.
You could create two classes that extend your User class, and then use the specific child class based on if you are marshalling on unmarshalling.
For example, for a class User:
public class User {
#XmlElement
private String value;
public User() { }
public User(String value) {
this.value = value;
}
}
You can have UserIn:
#XmlRootElement(name = "UserIn")
#XmlAccessorType(XmlAccessType.FIELD)
public class UserIn extends User {
public UserIn() { }
public UserIn(String value) {
super(value);
}
}
and UserOut:
#XmlRootElement(name = "UserOut")
#XmlAccessorType(XmlAccessType.FIELD)
public class UserOut extends User {
public UserOut() { }
public UserOut(String value) {
super(value);
}
}
Provide the appropriate class where you need, and you will get it working with the input or output you wish.

Creating xml from JAXB when one value is retrieved using a factory method

I have a UUID class where I get the uuid from a static factory method like
UUIDGenerator.getInstance().getUuid();
I have another class which has these UUids as a list.
class Artifact
{
Uuid uuid;
setUuid(Uuid uuid)
{
this.uuid = uuid;
}
Uuid getUuid()
{
return this.uuid;
}
}
Class ArtifactData
{
private List<Artifact> artifacts;
//setter for list
// getter for list
}
I want the xml to be created as
<ArtifactData>
<AssociatedArtifactList>
<ArtifactUuid>#some value<ArtifactUuid>
</AssociatedArtifactList>
</ArtifactData>
How do I create this xml out of Jaxb annotations. It complains on saying there isn't a public constructor for Uuid because it is constructed out of a factory method.
EDIT: The Uuid and Uuid generator cannot be modified. They are in a JAR. This is what I have tried so far
public class Artifact
{
#XmlJavaTypeAdapter(ArtifactUuidAdapter.class)
private Uuid uuid;
public Uuid getUuid()
{
return uuid;
}
public void setUuid(Uuid uuid)
{
this.uuid = uuid;
}
}
#XmlRootElement
public class ArtifactData
{
private List<Artifact> associatedArtifactList;
public List<Artifact> getArtifacts()
{
return associatedArtifactList;
}
#XmlElementWrapper(name="associatedArtifactList")
#XmlElement(name = "artifactUuid")
public void setArtifacts(List<Artifact> artifacts)
{
this.associatedArtifactList = artifacts;
}
}
public class ArtifactUuidAdapter extends XmlAdapter<Uuid, String>
{
#Override
public Uuid marshal(String uuid) throws Exception
{
return Uuid.getInstance(uuid);
}
#Override
public String unmarshal(Uuid uuid) throws Exception
{
return uuid.getData();
}
}
I still get an error called no arg default constructor is missing.
The #XmlType annotation allows you to configure a factory class and method. The factory class described in your questions doesn't quite meet the API requirements but you could easily create an adapter for it.
UUIDGeneratorAdapter
The class below would adapt your factory class to something that a JAXB (JSR-222) implementation could leverage.
public class UUIDGeneratorAdapter {
public static Uuid getUuid() {
return UUIDGenerator.getInstance().getUuid();
}
}
Uuid
Below is an example of how you configure the factory class through the #XmlType annotation.
import javax.xml.bind.annotation.XmlType;
#XmlType(factoryClass=UUIDGeneratorWrapper.class, factoryMethod="getUuid")
public class Uuid {
// ...
}
For More Information
http://blog.bdoughan.com/2011/06/jaxb-and-factory-methods.html

JAXB/MOXy: How to partially unmarshall, passing the descendants of a given node to a closed/proprietary class?

I'm starting with some Java classes that I would like to be able to unmarshall from XML--I'm determining the schema as I go. I would like to use XML similar to the following:
<Person fname="John" lname="Doe">
<bio><foo xmlns="http://proprietary.foo">Blah <bar>blah</bar> blah</foo></bio>
</Person>
I'm hoping to annontate my Java classes similar to the following:
public class Person {
#XmlAttribute
public String fname;
#XmlAttribute
public String lname;
#XmlElement
public ProprietaryFoo bio;
}
I'd like to pass the <foo xmlns="http://proprietary.foo"> element and it's descendants to a compiled factory class which works like this:
FooFactory.getFooFromDomNode(myFooElement) // Returns a private ProprietaryFooImpl as an instance of the public ProprietaryFoo Interface
It seems like I need to create a DomHandler for ProprietaryFoo but I'm not quite able to figure it out (I was getting “com.xyz.ProprietaryFooImpl nor any of its super class is known to this context.") I'm also interested in XmlJavaTypeAdapter I can't figure out how to receive the ValueType as an Element.
Ended up using both an XmlAdapter and a DomHandler along with a simple Wrapper class.
public class FooWrapper {
#XmlAnyElement(FooDomHandler.class)
public ProprietaryFoo foo;
}
public class FooXmlAdapter extends XmlAdapter<FooWrapper, ProprietaryFoo> {
#Override
public ProprietaryFoo unmarshal(FooWrapper w) throws Exception {
return w.foo;
}
#Override
public FooWrapper marshal(ProprietaryFoo f) throws Exception {
FooWrapper fooWrapper = new FooWrapper();
fooWrapper.foo = f;
return fooWrapper;
}
}
/* The vendor also provides a ProprietaryFooResult class that extends SAXResult */
public class FooDomHandler implements DomHandler<ProprietaryFoo, ProprietaryFooResult> {
#Override
public ProprietaryFooResult createUnmarshaller(ValidationEventHandler validationEventHandler) {
return new ProprietaryFooResult();
}
#Override
public ProprietaryFoo getElement(ProprietaryFooResult r) {
return r.getProprietaryFoo();
}
#Override
public Source marshal(ProprietaryFoo f, ValidationEventHandler validationEventHandler) {
return f.asSaxSource();
}
}
For whatever reason, this didn't work with the standard classes from the com.sun namespace but MOXy handles it well.

Resources