Создайте единый объект, используя новое ключевое слово

Приведенный ниже вопрос задает директор проекта в ходе 4-го тура собеседования.

Есть класс А. Любое количество классов может быть производным от A. Ограничения - это любой подкласс, производный от самого A или A, я должен иметь возможность создавать только один объект для каждого класса с использованием ключевого слова new. Если я попытаюсь создать другой объект, он выдаст исключение.

Создайте единый объект, используя новое ключевое слово

Класс B является производным классом от A, аналогично классам C, D, E также являются производные классы. Количество занятий не ограничено. Может быть получено любое количество классов.

Логика ограничения должна быть внутри иерархии классов, а не внутри основного класса.

Пример кода основного класса.

A obj1 = new A();  // object should create
A obj2 = new A();   // exception should throw
E Obj3 = new E();  //object should create
E obj4 = new E();   //Exception should throw

@Logan, который мог бы работать, но класс A также имеет подкласс, и при создании объекта подкласса будет вызван конструктор A.

Lone_Coder 29.05.2018 06:52

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

AxelH 29.05.2018 07:17

Я ценю быстрое возвращение.

GhostCat 22.09.2018 06:26
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
3
367
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

В классе A: иметь статический набор класса. Каждый раз, когда вызывается конструктор A, используйте this.getClass () для получения фактического класса, для которого требуется создать экземпляр (имейте в виду, что любой подкласс должен сначала вызвать суперконструктор).

Если класс хранится в наборе, вызовите это исключение. Если нет, сохраните класс.

public class A {

private static final Set<Class<? extends A>> INSTANCE_HOLDER = new HashSet<>();

public A() {
    if (INSTANCE_HOLDER.contains(this.getClass()))
        throw new RuntimeException("can't create more than one instance.");

    INSTANCE_HOLDER.add(this.getClass());
}
}

Этого должно быть достаточно, чтобы вы начали.

Для справки: хотя это должно сработать, это кажется довольно странной идеей. Если вам нужны одноэлементные объекты, лучше изучите, например, использование перечислений. Это предотвратит все тонкие проблемы, например, из-за создания объектов несколькими потоками.

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

Но в конечном итоге все это звучит как нарушение принципа единой ответственности.

Тем более, что это потребовало бы реализации finalize для освобождения класса от Set, когда GC собирает то, что он может, но, конечно, я бы не стал добавлять такую ​​логику в метод Finalize, поэтому, если Set не станет Map<Class, A>, чтобы быть "Mutliton `(Multi + singleton;)), это никогда не будет использоваться.

AxelH 29.05.2018 07:14

Вы можете использовать статический экземпляр в A, а затем в конструкторе проверить, существует ли экземпляр, и если да, то выбросить исключение, если он не создает объект. Я предполагаю, что существует только один объект любого класса, который является либо A, либо подклассом A. Если вы хотите сказать, что должен быть только экземпляр для каждого подкласса и A, тогда вам может потребоваться создать статический HashMap с классом Names в качестве ключа в A, а затем проверьте, существует ли экземпляр для конкретного класса, проверив className из HashMap.

Вы можете добиться этого, сохранив статическую ссылку на коллекцию, содержащую созданные экземпляры классов (может быть любая структура данных, которая работает), а затем проверив ее в конструкторе, чтобы избежать множественных экземпляров:

class A {
    private static Set<String> instantiatedClasses = new HashSet<>();

    A() {
        super();

        if (instantiatedClasses.contains(getClass().getName())) {
            throw new IllegalStateException(
                    "Cannot create multiple instances of " +getClass().getName());
        }

        instantiatedClasses.add(this.getClass().getName());
    }
}

class B extends A {
}

И когда это проверено:

A a = new A();
System.out.println("Created a: " + a);
try {
    a = new A();
} catch (Exception e) {
    e.printStackTrace();
}

a = new B();
System.out.println("Created b: " + a);
a = new B();

Создается такой вывод:

Created a: stackoverflow.A@506e1b77
java.lang.IllegalStateException: Cannot create multiple instances of stackoverflow.A
    at stackoverflow.A.<init>(Main.java:32)
    at stackoverflow.Main.main(Main.java:14)
Created b: stackoverflow.B@9807454
Exception in thread "main" java.lang.IllegalStateException: Cannot create multiple instances of stackoverflow.B
    at stackoverflow.A.<init>(Main.java:32)
    at stackoverflow.B.<init>(Main.java:40)
    at stackoverflow.Main.main(Main.java:21)

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

Существуют альтернативные способы отслеживания классов, экземпляры которых были созданы, один из которых хранит класс, но я считаю, что необходимая часть - это проверка типа в конструкторе (где можно увидеть классы среды выполнения, и пока не поздно, чтобы предотвратить успешное создание)

Вам нужен забавный обходной путь для вашего решения? a = new A(){};. Теперь, не может ли это все еще рассматриваться как класс A ... не совсем.

AxelH 29.05.2018 07:23

@AxelH Да ... Но это не отменяет ответа на вопрос об интервью, потому что new A(){} фактически представляет собой другой подкласс. Таким образом, создается только один его экземпляр.

ernest_k 29.05.2018 07:34

Полностью согласен. Но с этим я все еще могу сгенерировать столько экземпляров, сколько захочу. Я мог бы в цикле генерировать «бесконечное» количество анонимных классов до перегрузки Set. А поскольку это не тот класс, я не думаю, что было бы правильно просто отказаться от любого getClass().isAnonymous(). Это просто для того, чтобы указать, что вопрос сам по себе не идеален и для этого требуются некоторые границы.

AxelH 29.05.2018 08:08

@AxelH Верно. Вопрос, вероятно, был предназначен только для проверки навыков решения проблем. Это хороший пример бесполезной проблемы с ... Но вы не можете создать несколько экземпляров внутреннего класса в цикле. (на самом деле это тот же класс Main$1). Вы можете обойти это только путем динамического создания классов (например, с помощью javassist).

ernest_k 29.05.2018 08:18

Я этого не знал! (Никогда не приходил в голову честно говоря;))

AxelH 29.05.2018 09:02

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