HoelzelJon · GitHub

Hello,

I have a set of classes like the following:

interface A {
	...
}
@Immutable
@Modifiable
@Style(deepImmutablesDetection = true)
interface B extends A {
	...
}
@Immutable
@Modifiable
@Style(deepImmutablesDetection = true)
interface C extends A {
	...
}
@Immutable
@Modifiable
@Style(deepImmutablesDetection = true)
interface D {
	A getA();
	...
}

What I'd like here is the ability to guarantee that calling getA() on an ImmutableD can only return an ImmutableB or an ImmutableC (and likewise for Modifiable).

It is currently possible to do this by adding a custom builder and custom modifiable implementation for D (as shown here). However, this isn't workable for my use case since D is used in a number of other classes with deepImmutablesDetection, each of which would then need need their own custom Modifiable class (to ensure the ModifiableD they have is of type D.Modifiable).

@Immutable
@Modifiable
@Value.Style(deepImmutablesDetection = true, overshadowImplementation = true, create = "new")
interface D {
    A getA();
    class CustomBuilder extends ImmutableD.Builder {
        @Override
        public D build() {
            D d = super.build();
            if (d.getA() instanceof ModifiableB) {
                this.a(((ModifiableB) d.getA()).toImmutable());
                return super.build();
            } else if (d.getA() instanceof ModifiableC) {
                this.a(((ModifiableC) d.getA()).toImmutable());
                return super.build();
            } else {
                return d;
            }
        }
    }
    public static class Modifiable extends ModifiableD {
        @Override
        public Modifiable a(A a) {
            if (a instanceof ImmutableB) {
                super.a(ModifiableB.create().from((ImmutableB) a));
            } else if (a instanceof ImmutableC) {
                super.a(ModifiableC.create().from((ImmutableC) a));
            } else {
                super.a(a);
            }
            return this;
        }
    }
    ...
}

I have a couple ideas for how an improvement to this could look:

  • An annotation like @DeepImmutableDetectedSubclasses(B.class, C.class) on either interface A or the getA method that indicates which classes should be checked for converting Immutable<->Modifiable within the Modifiable and Builder implementations of D. This would very cleanly solve the issue I'm having, but it is admittedly only useful for my specific use case.
  • Add support for overriding the Modifiable version of a class (similar to the way overshadowImplementation = true allows this for Builder). This way, I could use the version of D above with custom builder and Modifiable implementations, but D.Modifiable would automatically be used in other Immutables-generated code instead of ModifiableD.

Read the original on github.com ↗