Java: вопрос о наследовании новичков

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

Возможно ли это?

8 ответов

не делать. Сделайте абзац метода "foo".

abstract class B {
 public abstract B foo();
}

Или получить абстрактный factory через конструктор базового класса:

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();
 }


Как говорят другие ответы, вы можете использовать getClass(). newInstance(), если в каждом подклассе есть конструктор без аргументов (убедитесь, что вы улавливаете InstantationException и 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 - это имя вашего базового класса.

Вам придется бросить его везде, где вы хотите его использовать, хотя... Я не уверен, что это действительно отличное решение!

Изменить: методы типа newInstance() обычно являются статическими, и, конечно, вы не будете иметь представления о том, какой тип вашего подкласса имеет статический метод.

Я не думаю, что любой способ получить статический метод (динамически) создать экземпляр подкласса.


Ну, я мог бы отключиться, но я бы предположил, что поскольку "this" всегда ссылается на текущий объект, вы можете сделать что-то вроде

public B foo() {
 return this.getClass().newInstance();
}

или что-то в этом роде? Если вы создадите экземпляр D, а затем вызовите d.foo(), вы должны получить экземпляр D, возвращаемый как B. Вы можете вернуть его как обычный объект, но вы должны быть как можно более конкретными в этом случае, я думаю.


Помимо того факта, что я думаю, что, возможно, есть недостаток дизайна, если вы хотите это сделать, вы можете попробовать следующий подход.

В вашем вопросе вы используете статические (класс) методы, 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(); 
 }
 }
}


@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>{ 
}
</c></x>

Позвольте мне предложить вам этот совет. Способ обучения CS-классам состоит в том, что профессора влюблены в наследство, но не определили состав. Я думаю, что вы можете искать композицию. Поэтому вместо вызова getNextThing на самом Thing, возможно, вам стоит подумать о том, чтобы сделать Thing внедренным Iterable

Это означает, что вам просто нужно написать Iterator, который может инкапсулировать логику получения следующей вещи, t, похоже, вписывается в вашу модель наследования. Другим преимуществом этого является то, что из этого вы получаете некоторые сильные синтаксические устройства (улучшенные для понимания цикла).

licensed under cc by-sa 3.0 with attribution.