Взятие String и создание объекта из класса, который String представляет в Java - PullRequest
1 голос
/ 07 июня 2011

Сценарий:

  • У меня есть два класса с именами ListLayout и GridLayout, которые оба реализуют CustomLayout.
  • Пользователь вводит строку, представляющую макет, который он хочет использовать (например, «ListLayout»)

Как я могу создать объект ListLayout на основе строки, введенной пользователем? Я должен был бы быть эквивалентен просто делать это:

CustomLayout layout = new ListLayout();

В идеале мне нужно было бы найти решение, которое позволило бы мне проверить, соответствует ли введенная строка предопределенному классу, который реализует CustomLayout, прежде чем фактически создавать объект (потому что он выдаст ошибку, если ее не существует, и я не проверяю заранее).

Это действительно заставляет меня задуматься .... заранее спасибо за вашу помощь

Ответы [ 9 ]

7 голосов
/ 07 июня 2011

Если вы не хотите использовать отражение здесь, карта фабрик может быть правильной:

interface LayoutFactory {

   public CustomLayout makeLayout();

}

Map<String, LayoutFactory> factories = new HashMap<String, LayoutFactory>();
factories.put("GridLayout", new LayoutFactory() {
    public CustomLayout makeLayout() { return new GridLayout(); }
});
factories.put("ListLayout", new LayoutFactory() {
    public CustomLayout makeLayout() { return new ListLayout(); }
});



String layoutName = ...; // user input
CustomLayout l = factories.get(layoutName).makeLayout();

Конечно, вы также должны обработать случай, когда пользователь дал неизвестное имя макета (factories.get затем возвращает null).

2 голосов
/ 07 июня 2011

Сделайте что-то вроде этого:

String userInput = //get user's input
while(!userInput.equalsIgnoreCase("ListLayout") && !userInput.equalsIgnoreCase("gridLayout")){
   System.out.println("Please enter a valid option");
   userInput = //get user's input again
}
CustomLayout layout;
if(userInput.equalsIgnoreCase("ListLayout")
   layout = new ListLayout();
else
   layout = new GridLayout();
2 голосов
/ 07 июня 2011

Я бы лично не рекомендовал использовать рефлексию; как рефакторинг становится болью.

Лучше было бы проверить строку и создать экземпляр правильного класса.

Например

if(userInput.equals("ListLayout")) {
    CustomLayout layout = new ListLayout();
} else if (userInput.equals("GridLayout")) {
    CustomLayout layout = new GridLayout();
}

Это также может быть реализовано с использованием Java-отражения, как отмечали другие. Но если вы хотите выполнить рефакторинг кода позже (например, с помощью рефакторинга eclipse), код рефакторинга не будет автоматически реорганизован. Например, если вы использовали отражение и если вы изменили имя класса ListLayout на FancyListLayout и выполнили автоматический рефакторинг затмения, код отражения останется нетронутым, и ваш код сломается.

2 голосов
/ 07 июня 2011

Это код, используемый для получения экземпляра класса, если у вас есть строка с полным именем:

try {
    CustomLayout layout = (CustomLayout) Class.forName("your.package.ListLayout").newInstance();
} catch (Exception ex) {

}

Исключение может быть типа: LinkageError, ExceptionInInitializerError,ClassNotFoundException, IllegalAccessException, InstantiationException и SecurityException и рекомендуется иметь предложения catch для каждого из них, если вы хотите обрабатывать их по-разному.

1 голос
/ 07 июня 2011

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

Также вместо:

if ("ListLayout".equals(userInput)) {
   return new ListLayout();
}

вы можете добавить защищенное поле внутри вашей базы Layout класс:

public abstract class Layout {
    protected String userInputName;
}

и изменив его расширители следующим образом:

public class ListLayout {

    public ListLayout() {
        userInputName = "listLayout"; // set protected field
    }
}

Тогда вы можете сделать:

for (Layout l : setOfAllLayouts) {
    if (userInput.equals(l.getInputName)) {
        return l.clone();
    }
}
1 голос
/ 07 июня 2011

Отражение - это естественный ответ, но вы можете создать для него Фабрику.

public class CustomLayoutFactory {
    public CustomLayout createInstance(String layoutName) {
        if("ListLayout".equals(layoutName) {
            return new ListLayout();
        } else if("GridLayout".equals(layoutName) {
            return new GridLayout();
        }
        return null;
    }
}

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

1 голос
/ 07 июня 2011

Хорошо. То, на что вы должны обратить внимание, называется Reflection ( вики на отражение ), и java предлагает богатый API для этого. Это в основном позволяет генерировать объекты из String и перехватывать исключение, если такого класса не существует. Это, однако, имеет некоторые недостатки, пожалуйста, проверьте API для дальнейшего использования.

Ура!

1 голос
/ 07 июня 2011

Два шага:

  1. Найти, если текст, введенный пользователем, соответствует существующему классу (это можно сделать, используя каркас отражений , я полагаю)
  2. Создание экземпляра объекта этого класса, что обычно делается с использованием Class.forName(String)
0 голосов
/ 07 июня 2011

Это должно сделать это:

Class.forName(userInput).newInstance();

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

...