Beryllium
Beryllium

Reputation: 12998

How to set super class properties in @Immutable classes?

Given

import groovy.transform.Immutable

class A {
  String a
}

@Immutable
class B extends A {
  String b
}

When I try to set the values using the map constructor

def b = new B(a: "a", b: "b")

then a ist still null:

groovy:000> b = new B(a: "a", b: "b")
===> B(b)
groovy:000> b.b
===> b
groovy:000> b.a
===> null

Is it possible to define an @Immutable class which considers the properties of its super classes in the map constructor?

I am using Groovy 2.4.3

Upvotes: 5

Views: 1126

Answers (2)

user2782001
user2782001

Reputation: 3488

I have run across this as well. You can get around it by calling the getter method groovy auto adds for you on the property instead of directly accessing the property (in your example b.getA() instead of b.a )

Here's a stupid example....

class base{
    def name;
    def myNameIs(){
         return name;//willl return null when called from extending class person
    }
    def extendersNameIs(){
       return getName()// will return extending class default value
    }
 }
 class person extends base{
     def name="betty"//override set default value
  }
  def m=new person();
  println m.myNameIs(); // outputs null
  println m.extendersNameIs();//outputs "betty"

See it in action https://groovyconsole.appspot.com/script/5193965758316544 This isn't always an ideal solution, but it's a workaround.

Upvotes: 0

bdkosher
bdkosher

Reputation: 5883

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.

I'm not sure if this omission is intentional or accidental; regardless, this class would need to be modified in order to achieve what you're looking for. In the meantime, my only suggestion is to use composition instead of inheritance with @Delegate to simplify access to A's properties.

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")
println b.a

However, since A is mutable (even though we declare it immutable in the @Immutable annotation, B becomes mutable, too, e.g.

b.a = 'foo' // succeeds

Upvotes: 4

Related Questions