I have a (for me) complex Java generics problem. I read through some documentation and understand some but certainly not all of what I should. Basically, for me, trying to solve it would result in try and error.
In the following, I give a condensed example of my code, once without any generics (so one can hopefully understand what I want to achieve) and the other with some additions that come closer to the solution. Please correct my second version and/or point me to specific documentation. (I have general documentation of Java generics. But my code seems to have several interfering challenges and it is hard to a correct solution)
About my example: There is an abstract base type and several implementing variants (only one is given). Method combine()
calls getOp1()
, which decides (depending on <some condition>
) if it should operate on its own instance or on a new one. After the calculation, it returns the target instance.
abstract class Base {
protected final Base getOp1() {
if(Util.isConditionMet()) { return getNewInstance(); }
else { return this; }
}
protected abstract Base getNewInstance(); // returns a new instance of an implementing class
public abstract Base combine(Base other);
}
class Variant extends Base {
public Variant getNewInstance() { return new Variant(); }
public combine(Variant op2) {
Variant op1 = getOp1();
op1.calculate(op2);
return op1;
}
private void calculate(Variant other) { /* some code */ }
}
The version with some generics added. This version is faulty and does not compile.
abstract class Base<T extends Base<T>> {
protected final T getOp1() {
if(Util.isConditionMet()) { return getNewInstance(); }
else { return this; }
}
protected abstract T getNewInstance(); // returns a new instance of an implementing class
public abstract T combine(T other);
}
class Variant<T extends Variant<T>> extends Base<T> {
protected T getNewInstance() { return new Variant(); }
public T combine(T op2) {
T op1 = getOp1();
op1.calculate(op2);
return op1;
}
private void calculate(T other) { /* some code */ }
}
I have seen a couple of such combinational, operational classes, though never too elaborate. Maybe inheritance is not the right tool.
Better to use a lookup mechanism for capabilities, features.
class Base {
// Untyped
private Map<Class<?>, Object> capabilities = new HashMap<>();
protected <I> void register(Class<I> intf, I obj) {
capabilities.put(intf, obj);
}
public <T> Optional<T> lookup(Class<T> intf) {
Object obj = capabilities.get(intf);
return obj == null ? Optional.emtpy() : Optional.of(intf.cast(obj));
}
}
interface Flying {
void fly(double altitude);
}
Base pelican = new Pelican();
Flying flying = pelical.lookup(Flying.class).orElse(null);
flying.fly(0.5);
This also allows dynamic changes, and combining things with respect to two aspects.
To make this code working, you need to resolve incompatibility type issues: replace T
returning types by Base<T>
and cast result of Variant#getOp1()
to Variant<T>
to allow invoke calculate()
on it (this is safe here because Variant#getOp1()
always returns Variant
:
abstract class Base<T extends Base<T>> {
protected final Base<T> getOp1() {
return condition() ? getNewInstance() : this;
}
protected abstract Base<T> getNewInstance();
public abstract Base<T> combine(T other);
}
class Variant<T extends Variant<T>> extends Base<T> {
protected Base<T> getNewInstance() {
return new Variant();
}
public Base<T> combine(T op2) {
Variant<T> op1 = (Variant<T>) getOp1(); // <- explicit cast
op1.calculate(op2);
return op1;
}
private void calculate(Base<T> other) {
// ...
}
}
Btw, I still see no reason of such complicated type structure.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.