Reputation: 1524
This has probably been asked a million times before, but I'm having trouble wrapping my head around writing a copy constructor on an abstract class with a bounded type parameter. I have some code that looks like this:
public abstract class Superclass<T> {
Set<? extends Variable<T>> vars;
public abstract Superclass<? extends T> copy();
class Variable<T> {
T value;
}
}
class Foo extends Superclass<Integer> {
public Foo copy() {
Foo _newFoo = Foo();
Set<FooVariable> _newVars = new HashSet<FooVariable>();
_newVars.addAll(this.vars);
_newFoo.vars = _newVars;
}
class FooVariable extends Variable<Integer> { /* ... */ }
}
class Bar extends Superclass<String> {
public Bar copy() {
Bar _newBar = Bar();
Set<BarVariable> _newVars = new HashSet<BarVariable>();
_newVars.addAll(this.vars);
_newBar.vars = _newVars;
}
class BarVariable extends Variable<String> { /* ... */ }
}
Since the copy
method for both Foo
and Bar
is the same except for the variable types, I'd like to be able to move that code into a concrete method in the superclass. But I can't figure out (a) how to have the concrete public Superclass<? extends T> copy
method return a Foo
instance if called on a Foo
and a Bar
instance if called on a Bar
and (b) populate the vars
set with FooVariable
s or BarVariable
s as appropriate.
Can anybody please help and tell me what I'm missing? Thanks.
Upvotes: 13
Views: 2021
Reputation: 11481
What about this kind of Superclass
?
public abstract class Superclass<T> {
Set<? extends Variable<T>> vars;
public Superclass<? extends T> copy() {
Superclass<T> _newSuperclass = this.getNewInstance();
Set<Variable<T>> _newVars = new HashSet<Variable<T>>();
_newVars.addAll(this.vars);
_newSuperclass.vars = _newVars;
return _newSuperclass;
}
public abstract Superclass<T> getNewInstance();
class Variable<T> {
T value;
}
}
The point is that you just need to implement getNewInstance()
in subclasses instead of constructor.
So Foo
would look just like:
class Foo extends Superclass<Integer> {
@Override
public Superclass<Integer> getNewInstance() {
return new Foo();
}
class FooVariable extends Variable<Integer> { /* ... */ }
}
Upvotes: 2
Reputation: 9775
Tested this code, it has a couple of warnings, but it does what you want:
public <C extends Superclass<? extends T>> C copy() throws InstantiationException, IllegalAccessException {
C result= (C) this.getClass().newInstance();
HashSet newVars= new HashSet();
newVars.addAll(this.vars);
result.vars= newVars;
return result;
}
One remark: this is not a copy constructor. This is just a copy method. Constructor does not have a return type and its name is equal to class name.
Upvotes: 0
Reputation: 178263
Introduce a second generic type parameter to represent the Variable<T>
, U
.
Then, Foo.FooVariable<T>
and Bar.BarVariable<T>
satisfy the bounds of U
and can be returned from the copy
method.
EDIT
I've changed the code to move the implementation of copy
into the superclass. It relies on a newInstance
method (introduced already by @OndrejBozek).
public abstract class Superclass<T, U extends Variable<T>> {
Set<U> vars;
class Variable<T> {
T value;
}
public Superclass<T, U> copy() {
Superclass<T, U> _newSuperclass = newInstance();
Set<U> _newVars = new HashSet<U>();
_newVars.addAll(vars);
_newSuperclass.vars = _newVars;
return _newSuperclass;
}
public abstract Superclass<T, U> newInstance();
}
class Foo extends Superclass<Integer, Foo.FooVariable> {
public Foo newInstance() { return new Foo(); }
class FooVariable extends Variable<Integer> { /* ... */ }
}
class Bar extends Superclass<String, Bar.BarVariable> {
public Bar newInstance() { return new Bar(); }
class BarVariable extends Variable<String> { /* ... */ }
}
Upvotes: 0