Джава:Вопрос о наследстве для новичков
-
03-07-2019 - |
Вопрос
Предположим, у меня есть базовый класс B и производный класс D.Я хочу иметь в своем базовом классе метод foo(), который возвращает новый объект любого типа, который является экземпляром.Так, например, если я вызываю B.foo(), он возвращает объект типа B, а если я вызываю D.foo(), он возвращает объект типа D;между тем реализация находится исключительно в базовом классе B.
Это возможно?
Решение
Не.Сделайте метод «foo» абстрактным.
abstract class B {
public abstract B foo();
}
Или получить абстрактную фабрику через конструктор базового класса:
abstract class B {
private final BFactory factory;
protected B(BFactory factory) {
this.factory = factory;
}
public B foo() {
return factory.create();
}
}
interface BFactory {
B create();
}
Добавьте ковариантные возвращаемые типы и дженерики по вкусу.
Другие советы
Пока у каждого класса есть конструктор по умолчанию:
public B instance() throws Exception {
return getClass().newInstance();
}
Ну, я мог бы быть выключен, но я бы предположил, что, поскольку "это" всегда ссылается на текущий объект, вы можете сделать что-то вроде
public B foo() {
return this.getClass().newInstance();
}
или что-то в этом роде? Если вы создаете экземпляр D и затем вызываете d.foo (), вы должны получить экземпляр D, возвращенный как B. Вы можете вернуть его как простой объект, но я думаю, что вы должны быть настолько конкретны, насколько это возможно, в этом случае, я думаю. р>
Помимо того, что я думаю, что, вероятно, есть недостаток дизайна, если вы хотите достичь этого, вы можете попробовать следующий подход.
В вашем вопросе вы используете статические (class) методы, B.foo (), D.foo (), это невозможно сделать с помощью наследования, потому что статические методы не имеют динамической природы, они не участвуют в поисковой системе. Таким образом, у вас недостаточно информации о типе.
Если вы используете функцию-член foo (), у вас может быть следующая конструкция:
public class B {
public B foo()
throws IllegalAccessException, InstantiationException {
return this.getClass().newInstance();
}
}
public class D extends B{
}
public class Test {
public static final void main(String[] args)
{
try {
System.out.println((new B()).foo());
System.out.println((new D()).foo());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
Как говорят другие ответы, вы можете использовать getClass().newInstance(), если в каждом подклассе есть конструктор без аргументов (обязательно поймайте InstantiationException и IllegalAccessException).
Если какой-либо из конструкторов требует аргументов, вы можете либо использовать отражение, либо (предпочтительно, на мой взгляд) определить метод типа getNewInstance(), который вы можете переопределить в подклассе только при необходимости.
например
Thing foo() {
Thing th = getNewInstance();
// do some stuff with th
return th;
}
Thing getNewInstance() {
return getClass().newInstance();
}
Тогда getNewInstance() можно переопределить только в том случае, если вам действительно нужно, для подклассов, у которых нет конструктора по умолчанию.
Thing getNewInstance() {
return new BigThing(10, ThingSize.METRES);
}
Я думаю, что это возможно сделать с помощью отражения, т. е. в вашем суперклассе есть:
public ClassName getFoo() throws InstantiationException, IllegalAccessException
{
return getClass().newInstance();
}
Где ClassName - это имя вашего базового класса.
Вам придется разыгрывать его там, где вы хотите его использовать, хотя ... Я не уверен, что это действительно отличное решение!
Edit: методы типа newInstance () обычно являются статическими, и, конечно же, вы не будете иметь представление о том, какой тип вашего подкласса имеет статический метод.
Я не думаю, что есть способ получить метод static для (динамического) создания экземпляра подкласса.
@Рик
Ну, настоящая проблема в том, что у меня есть абстрактный базовый класс Thing.И у Thing есть метод getNextThing(), который возвращает новый экземпляр Thing.
Кроме того, у меня есть несколько подклассов, таких как BigThing, LittleThing, SomethingThing, и я не хочу продолжать переписывать метод getNextThing() для каждого из них.Это кажется расточительным, поскольку все они делают одно и то же...вещь.
Я не прав в своем подходе?
Я не уверен, почему вы пытаетесь сделать то, что вы на самом деле пытаетесь сделать. Предоставление большего количества контекста может позволить нам оказать вам дополнительную помощь.
public class B <X extends B>{
public X foo() throws InstantiationException, IllegalAccessException{
return (X)this.getClass().newInstance();
}
}
public class C extends B<C>{
}
Позвольте мне предложить вам этот совет. Классы CS, как правило, преподаются в том, что профессора влюблены в наследство, но не выяснили состав. Я думаю, что вы можете искать это композиция. Поэтому вместо вызова getNextThing для самого Thing, возможно, вам следует подумать о том, чтобы заставить Thing реализовать Итерируемый
Это означает, что вам просто нужно написать Итератор , который может инкапсулировать логику получения следующей вещи, так как он, кажется, не вписывается в вашу модель наследования. Еще одним преимуществом этого является то, что вы получаете несколько хороших синтаксических устройств из этого (улучшено для понимания цикла).