Вопрос

Предположим, у меня есть базовый класс 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 реализовать Итерируемый

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top