Конструкторы Java Copy с дженериками
Это, вероятно, задавали миллионы раз раньше, но у меня возникают проблемы с записью конструктора копий в абстрактном классе с параметром ограниченного типа. У меня есть код, который выглядит так:
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> { /* ... */ }
}
Так как метод copy
для Foo
и Bar
является тем же, за исключением типов переменных, я хотел бы иметь возможность переместить этот код в конкретный метод в суперклассе. Но я не могу понять (а), как иметь конкретный метод public Superclass<? extends T> copy
, возвращать экземпляр Foo
, если он вызван в экземпляре Foo
и Bar
, если вызывается в Bar
и (b) vars
установить с FooVariable
или BarVariable
, если это необходимо.
Может ли кто-нибудь помочь и рассказать мне, что мне не хватает? Спасибо.
Ответы
Ответ 1
Как насчет этого типа 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;
}
}
Дело в том, что вам просто нужно реализовать getNewInstance()
в подклассах вместо конструктора.
Итак, Foo
будет выглядеть так:
class Foo extends Superclass<Integer> {
@Override
public Superclass<Integer> getNewInstance() {
return new Foo();
}
class FooVariable extends Variable<Integer> { /* ... */ }
}
Ответ 2
Ввести второй тип типичного типа для представления Variable<T>
, U
.
Тогда Foo.FooVariable<T>
и Bar.BarVariable<T>
удовлетворяют границам U
и могут быть возвращены из метода copy
.
ИЗМЕНИТЬ
Я изменил код, чтобы переместить реализацию copy
в суперкласс. Он опирается на метод newInstance
(введенный уже @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> { /* ... */ }
}
Ответ 3
Протестировав этот код, у него есть несколько предупреждений, но он делает то, что вы хотите:
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;
}
Одно замечание: это не конструктор копирования. Это всего лишь метод копирования. Конструктор не имеет типа возврата и его имя равно имени класса.