Предположим, у меня есть базовый класс B и производный класс D. Я хочу иметь в моем базовом классе метод foo (), который возвращает новый объект любого типа, которым является экземпляр. Так, например, если я вызываю B.foo (), он возвращает объект типа B, а если я вызываю D.foo (), он возвращает объект типа D; Между тем реализация находится исключительно в базовом классе B.
Это возможно?
Если вы зададите вопрос для новичков, вы сможете получить более ценные ответы, в которых будет указана основная проблема. Я считаю, что должно быть лучшее решение вашей проблемы, чем B и D, которые вы использовали в своем вопросе.




Пока у каждого класса есть конструктор по умолчанию:
public B instance() throws Exception {
return getClass().newInstance();
}
Это работает, но не объявляйте методы, которые генерируют общий класс «Exception».
Я придерживаюсь противоположного подхода - объявляю множественные исключения только тогда, когда клиент а) позаботится и б) восстановит свое состояние по-другому. В противном случае это просто визуальный беспорядок, например. демонстрации и тесты JUnit ...
Я думаю, что это можно сделать с помощью отражения, т.е. в вашем суперклассе у вас есть:
public ClassName getFoo() throws InstantiationException, IllegalAccessException
{
return getClass().newInstance();
}
Где ClassName - это имя вашего базового класса.
Вам придется использовать его везде, где вы хотите его использовать ... Я не уверен, что это действительно отличное решение!
Методы типа Редактировать: newInstance () обычно являются статическими, и, конечно, вы не будете иметь представления о типе вашего подкласса с помощью статического метода.
Я не думаю, что есть способ заставить метод статический (динамически) создать экземпляр подкласса.
Что ж, я мог бы быть отключен, но я бы предположил, что, поскольку «this» всегда относится к текущему объекту, вы можете сделать что-то вроде
public B foo() {
return this.getClass().newInstance();
}
Или что-то вдоль этих линий? Если вы создаете экземпляр D, а затем вызываете d.foo (), вы должны получить экземпляр D, возвращенный как B. Вы можете вернуть его как простой объект, но я думаю, вы должны быть как можно более конкретными в этом случае.
@Rik
Что ж, настоящая проблема в том, что у меня есть абстрактный базовый класс 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 реализовал Итерабельный
Это означает, что вам просто нужно написать Итератор, который может инкапсулировать логику получения следующего объекта, поскольку он, похоже, не вписывается в вашу модель наследования. Еще одно преимущество этого состоит в том, что вы получаете из этого несколько хороших синтаксических приемов (улучшенных для понимания цикла).
Помимо того факта, что я думаю, что, вероятно, есть недостаток дизайна, если вы хотите этого добиться, вы можете попробовать следующий подход.
В вашем вопросе вы используете статические (классовые) методы, 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();
}
}
}
Извините, я не имел в виду, что foo () был статическим методом. Это не.
Кроме того, почему вы считаете, что это результат недостатка дизайна?
Как говорят другие ответы, вы можете использовать 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);
}
Не надо. Сделайте метод "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();
}
Добавьте ковариантные возвращаемые типы и дженерики по вкусу.
Есть ли конкретная причина, по которой вы хотите сохранить реализацию в базовом классе?