Inheritance in Collections in Java

There is a problem when doing inheritance on collections in Java. It is about the classes about BmmDecisionGroup, etc.

They all have the property collection branches, and the parametrized class in the collection also has a type which is inheritable from along the same line.

The Java architects decided long ago that this does not work, they always kept it that way. I stumbled on it a few times already in the past, because I forget it and then rediscover it.

This does not work, compiler error, although BmmConditionBranch is inherited from BmmDecisionBranch:

ArrayList[BmmDecisionBranch] branches = new ArrayList [BmmConditionBranch] ();

You can make it to work like this:

ArrayList[? extends BmmDecisionBranch] branches = new ArrayList [BmmConditionBranch] ();

This works oke, but you cannot have methods with the collections as parameter,
For example, this does not work:

public void addBranches(List [BmmConditionBranch] branches) {
          this.branches.addAll(branches);
 }

This how to solve it

public void addBranches(List [? extends BmmDecisionBranch] branches) {
        this.branches.addAll(branches);
    }

But then typesafety in the inherited classes is a problem. There is no such thing as redefine in Java in parametrized Collections with inheritance on the parametrized class.

It is also explained here:

Just telling you, there are solutions for it to remain typesafe, but that is not really clean code then. And I heard other compilers for Eiffel or C++ do not have these problems.

But maybe someone has an idea?

My understanding is, you’re asking if there is a way to achieve non-invariant generics with Java.

In that case, no, that is not possible. Java generic types are invariant and for good reason. C# generic types are also invariant.

You can force variance (covariance and contravariance) as you’re doing, but the compiler will make sure that you cannot create potentially dangerous situations. Same goes for C# and Scala.

openEHR RM uses inheritance and generics together to achieve semantics of ‘redefining’ types towards the bottom of type hierarchy and that does not play nice with the generic type implementation of mainstream languages.

Kotlin can give you a bit more room on the JVM, but that’s it.

I’m aware of only two languages that adopt variant generics, Eiffel and Dart. These languages make a design choice and rely on static analysis and/or the programmers’ ability to avoid various patterns of potential bugs.

As you can see, it gets subjective, so I won’t comment on which language designers get it right. That’s a flamewar.

In your case, if you’re using Java, you’re stuck with this choice. Take a look at how Archie uses Java’s generic to model RM types, see quantity package for example. It is the same situation and they’ve done a good job. You can adopt their patterns if you already have not, but at the end of the day, this is what you’ll have to live with given your language of choice (and mine for that matter).

Just trying to save you some time. Hope it helps.

Pretty much everybody here is a better Java developer than I’ll ever be, but I am currently working (in my favourite language) on Archie (under the stern supervision of @pieterbos :wink: and am currently thinking on these kinds of problems myself. So you may feel free to correct any egregious errors I may make below or generally on the topic …

My view on the UML is that it is better to keep it simple and indicate as purely as possible the design intention, including things like multiple inheritance and covariant redefinition. I would not like to rewrite the models for Java, when C#, although similar, is different (e.g. it has things like virtual properties etc, from memory), but more to the point, I have the feeling that we’ll all be using Kotlin instead of Java inside 5y, and Kotlin’s type rules are definitely not the same as Java’s.

However, the specs are light on implementation patterns in well-known languages. We used to have a ‘Java ITS’ kind of document, and we probably should remedy this situation.

For now, the pattern that I believe is the correct one to model such as BMM_DECISION_GROUP.branches as you see here in the BMM is ot use Java generics, e.g. you would define

class BmmDecisionGroup<T extends BmmDecisionBranch> extends BmmStatement {}

class BmmConditionChain extends BmmDecisionGroup<BmmConditionBranch> {}

class BmmCaseGroup extends BmmDecisionGroup<BmmCaseBranch> {}

Then various properties and routines defined in the first class become generic, like:

private List<T> branches;
public addBranches(List<T> branches);

and similar. I think this will achieve the effect of covariant redefinition that we need in this part of the model. It is what we are using / will use elsewhere.

Not sure if I have addressed the problem completely but it seems to me this gets part of the way.

Thank you Seref and Thomas for your answers.

My son died today, 21 years of age, so it will take some weeks before I am able to understand your answers

But I will come back to this, some time.

Best regards
Bert Verhees

Thanks for your reply, Seref and Thomas, I believe I already used the proposed pattern, I must check it well. It is the only way, thanks for the confirmation.

But the problem of type-safety remains or we have parameterized abstract classes where they are not defined in specs, so we cannot use the abstract parent-classes as argument but we can handle that on the interface side of the code.

Thanks again.

PS: Small mistake, you write: Rename EL_ASSERTION to EL_BOOLEAN_EXPRESSION ;
(but you didn’t)
Good thing you made the classes clickable :wink:

Best regards
Bert Verhees