Создание экземпляра одноэлементного объекта с использованием Class.forName ()? - PullRequest
5 голосов
/ 15 июля 2009

Я хочу создать экземпляр одного экземпляра класса из строки Наименование класса. (используя Class.forName (). newInstance ().)

Вот проблема: я хочу, чтобы этот экземпляр был единичным. Я мог бы сделать это с помощью шаблона синглтона, за исключением того, что newInstance вызывает конструктор по умолчанию для класса, и с одиночным, этот конструктор должен быть "частным" ..

Есть ли решение? Я мог бы придумать менее элегантный способ сделать это (используя hashmap в качестве таблицы поиска ..), но предпочел бы лучшее решение ..

Спасибо

Ответы [ 5 ]

8 голосов
/ 15 июля 2009

Классический синглтон также имеет статический метод getInstance () - вызывайте его с помощью отражения вместо использования newInstance (). Да, это больше работы, но это так ...

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

В-третьих, вы можете вообще не иметь синглтона и найти лучшее решение (как правило, есть).

8 голосов
/ 15 июля 2009

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

Class c = Class.forName("test.MyClass");
Method factoryMethod = c.getDeclaredMethod("getInstance");
Object singleton = factoryMethod.invoke(null, null);

А потом

public class MyClass {

   private static MyClass instance;

   private MyClass() { 
      // private c'tor 
   }

   public static synchronized MyClass getInstance() {
      if (instance == null) {
         instance = new MyClass();
      }
      return instance:
   }
}

Предупреждение. Шаблон проектирования Singleton может нанести вред вашему долгосрочному здоровью.

2 голосов
/ 15 июля 2009

Вам нужно знать имя метода вашего одноэлементного класса, который создает объект. Если это, например, называется "instance ()", вы можете сделать что-то вроде

Class c = Class.forName("MySingleton");
Method m = c.getDeclaredMethod("instance",null);
MySingleton singleton = m.invoke(null,null);
1 голос
/ 15 июля 2009

Вы можете использовать enum и иметь HashMap для каждого из них. Или вы можете использовать какую-то инфраструктуру внедрения зависимостей, чтобы получить что-то одно (Guice?)

Редактировать Хорошо, я тоже использую пример кода:

package tests;
public class EnumSingleton {
    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("tests.Singleton1");
        Operation instance = Operation.class.cast(c.getEnumConstants()[0]);
        System.out.println(instance.getTHEAnswer());

        c = Class.forName("tests.Singleton2");
        instance = Operation.class.cast(c.getEnumConstants()[0]);
        System.out.println(instance.getTHEAnswer());
    }
}
interface Operation {
    int getTHEAnswer();
}
enum Singleton1 implements Operation {
    INSTANCE;
    @Override
    public int getTHEAnswer() {
        return 42;
    }
}
enum Singleton2 implements Operation {
    INSTANCE;
    @Override
    public int getTHEAnswer() {
        return 84;
    }
}

И это в целости и сохранности. Редактировать И теперь имеет некоторое значение.

0 голосов
/ 15 июля 2009

Можете ли вы вместо этого полагаться на существование статического фабричного метода? Он будет опираться на какое-то соглашение об именах, но это сработает.

Итог: если известно, что класс одноэлементный, то он должен нести ответственность за эту политику. Следовательно, он должен соответствовать некоторой реализации шаблона, и если вы хотите динамический выбор среди таких классов, то вы должны иметь возможность предсказать подход «Фабрика».

...