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

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

Это возможно?

Есть ли конкретная причина, по которой вы хотите сохранить реализацию в базовом классе?

Rik 03.10.2008 17:33

Если вы зададите вопрос для новичков, вы сможете получить более ценные ответы, в которых будет указана основная проблема. Я считаю, что должно быть лучшее решение вашей проблемы, чем B и D, которые вы использовали в своем вопросе.

Olaf Kock 03.10.2008 17:55
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
2
491
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Пока у каждого класса есть конструктор по умолчанию:

    public B instance() throws Exception {
        return getClass().newInstance();
    }

Это работает, но не объявляйте методы, которые генерируют общий класс «Exception».

jsight 03.10.2008 18:19

Я придерживаюсь противоположного подхода - объявляю множественные исключения только тогда, когда клиент а) позаботится и б) восстановит свое состояние по-другому. В противном случае это просто визуальный беспорядок, например. демонстрации и тесты JUnit ...

Garth Gilmour 03.10.2008 19:44

Я думаю, что это можно сделать с помощью отражения, т.е. в вашем суперклассе у вас есть:

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 () для каждого из них. Это кажется расточительным, поскольку все они делают одно и то же ...

Я ошибаюсь в своем подходе?

Нет, используя ответы на размышления, данные на ваш вопрос, вы сможете реализовать это так, как хотите, что мне кажется нормальным.

MattC 03.10.2008 17:39

Думаю, тогда тебе придется заняться размышлениями.

Rik 03.10.2008 17:47

«Это кажется расточительным, поскольку все они делают одно и то же ...». Это редко бывает расточительно. У вас есть подклассы не просто так. Часто у каждого подкласса есть свой конструктор, инициализация или что-то в этом роде. Они редко бывают идентичными.

S.Lott 03.10.2008 17:51

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

Robin 03.10.2008 17:53

Я не уверен, почему вы пытаетесь делать то, что на самом деле пытаетесь сделать. Если мы предоставим больше контекста, мы сможем оказать вам дополнительную помощь.

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 () был статическим методом. Это не.

Jake 03.10.2008 17:46

Кроме того, почему вы считаете, что это результат недостатка дизайна?

Jake 03.10.2008 17:49

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

Добавьте ковариантные возвращаемые типы и дженерики по вкусу.

Другие вопросы по теме