Reputation: 32333
I have a class hierarchy that looks a little bit like this. Note, my doesn't really look like this, I just wrote it this way to make it shorter and, in my opinion, more legible.
public abstract BaseClass {
private Alpha a;
private Beta b;
public Alpha getAlpha() { return a; }
public Beta getBeta() { return b; }
public BaseClass setAlpha(Alpha a) { this.a = a; return this; }
public BaseClass setBeta(Beta b) { this.b = b; return this; }
}
public Foo extends BaseClass {
@Override
public Foo setBeta(Beta b) {
super.setBeta(b);
doSomethingElse();
return this;
}
public Gamma getGamma() { return g; }
public Foo setGamma(Gamma g) { this.g = g; return this; }
}
public Bar extends BaseClass {
private Omega o;
@Override
public Bar setAlpha(Alpha a) {
super.setAlpha();
doSomethingElse();
return this;
}
public Omega getOmega() { return o; }
public Bar setOmega(Omega o) { this.o = o; return this; }
}
There are many more of these methods then listed above. Notice that they don't conform to the same interface. The classes that use Foo
and Bar
and need to call setGamma
and setOmega
always have access to the most specific class. However, those same classes also need to call setAlpha
and setBeta
. Those methods don't return the correct type, however, unless I override them.
Options:
BaseClass
just to change the return type
setGamma
and setOmega
abstract in BaseClass
. This might work, except then I'd have to define it for classes where it really makes no sense at all.throw new UnsupportedOperationException()
Exception
.someBar.setOmega().setAlpha()
but not someBar.setAlpha().setOmega()
All of these options seem gross. Does anyone have anything better?
Note: this situation is not quite like this one: Java - Inherited Fluent method return type to return incident class' type, not parent's as it's not really a generics issue.
Upvotes: 2
Views: 606
Reputation: 41135
As @chrylis noted in the comments, you can genericize BaseClass
:
public abstract class BaseClass<T extends BaseClass> {
private Alpha a;
private Beta b;
public Alpha getAlpha() {
return a;
}
public Beta getBeta() {
return b;
}
@SuppressWarnings("unchecked")
public T setAlpha(Alpha a) {
this.a = a;
return (T) this;
}
@SuppressWarnings("unchecked")
public T setBeta(Beta b) {
this.b = b;
return (T) this;
}
}
and extend as follows:
public class Foo extends BaseClass<Foo> {
private Gamma g;
@Override
public Foo setBeta(Beta b) {
super.setBeta(b);
doSomethingElse();
return this;
}
private void doSomethingElse() { }
public Gamma getGamma() {
return g;
}
public Foo setGamma(Gamma g) {
this.g = g;
return this;
}
}
public class Bar extends BaseClass<Bar> {
private Omega o;
@Override
public Bar setAlpha(Alpha a) {
super.setAlpha(a);
doSomethingElse();
return this;
}
private void doSomethingElse() { }
public Omega getOmega() {
return o;
}
public Bar setOmega(Omega o) {
this.o = o;
return this;
}
}
This introduces a couple unchecked cast warnings, which I suppressed. It compiles, but I haven't tried actually doing anything with it.
Upvotes: 3