Вызов общего типа с использованием строковых имен классов
Надеюсь, вы можете помочь мне с этим: у меня есть...
- список строк имен классов
classNameList
- общий класс
Geography<T>
- статический универсальный метод
<T> void read(Class<T> cl, Geography<T> geo)
Я хочу просмотреть список имен строковых классов и вызвать универсальный метод для каждого из этих классов.
То, что я пробовал, но, очевидно, не сработало:
for (int i = 0; i < classNameList.length; i++) {
Class<?> myClass = Class.forName(classNameList[i].getName());
Geography<myClass.newInstance()> geo;
read(myClass, geo);
}
Ошибка: myClass.newInstance не может быть разрешен для типа
Мой код отлично работает для одного вызова универсальной функции:
Geography<ExampleClass> ExampleGeo;
read(ExampleClass.class, ExampleGeo);
Есть идеи, как я мог это сделать?
ОБНОВИТЬ:
Спасибо за полезный вклад, но мне все еще трудно адаптировать его к моему реальному коду. Так что это не просто проблема:
Я делаю готовые в shapefile-Data с shapefileLoader, для каждой функции Shapefile класс (GuadAgent) инициализируется с предопределенным классом (PlantWind). В моем входном каталоге у меня есть шейп-файлы с именами классов, которые они представляют. Я хочу, чтобы Java читала в шейп-файлах и создала соответствующий класс агента. (агенты также помещаются в контекст и географию.) Используемые классы: ShapefileLoader, Geography, другие классы можно найти на том же веб-сайте.
Эта часть находится в main-методе:
Geography<GuadAgent> guadGeography = GeographyFactoryFinder.createGeographyFactory(null).createGeography("guadGeography", context, new GeographyParameters<GuadAgent>());
Context<GuadAgent> context = new DefaultContext<GuadAgent>();
FileFilter filter = new FileFilter() {
@Override
public boolean accept(File file) {
return file.getName().endsWith(".shp"); // return .shp files
}
};
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
File folder = new File(shapefileDir);
File[] listOfFiles = folder.listFiles(filter);
for (File classFile : listOfFiles) {
try {
readForName(classFile,context,guadGeography);
} catch (ClassNotFoundException | MalformedURLException
| FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Статический метод, который читает в именах:
static <T> void readForName(File classFile, Context<GuadAgent> context,Geography<GuadAgent> guadGeography) throws ClassNotFoundException, MalformedURLException, FileNotFoundException {
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
String className = classFile.getName().split("\\.(?=[^\\.]+$)")[0];
File shapefile = null;
shapefile = new File(shapefileDir+classFile.getName());
if (!shapefile.exists()) {
throw new FileNotFoundException("Could not find the given shapefile: " + shapefile.getAbsolutePath());
}
switch (className) {
case "PlantWind":
ShapefileLoader<PlantWind> PlantWindLoader = new ShapefileLoader<PlantWind>(PlantWind.class,shapefile.toURI().toURL() , guadGeography, context);
PlantWindLoader.load();
PlantWindLoader.close();
System.out.println(context.getObjects(PlantWind.class).size());
break;
// Todo Add other Agent types
default:
break;
}
Как я могу избавиться от выключателя? Хотя их число конечно, очень много разных агентов...
3 ответа
К сожалению, нет синтаксиса, близкого к вашему намерению (хотя хорошая идея).
Основная проблема заключается в том, что Class.forName()
возвращает неизвестное Class<?>
так что вам нужен актерский состав где-нибудь. Это просто вопрос того, где вы положили это.
Я предлагаю этот подход (который компилируется), который объединяет выполнение read()
на основе имени класса:
static <T> void readForName(String className) throws ClassNotFoundException {
Class<T> myClass = (Class<T>) Class.forName(className);
Geography<T> geo = new Geography<T>(); // No code shown. Adjust as required
read(myClass, geo);
}
Могу ли я также предложить использовать синтаксис цикла foreach для более понятного кода:
for (String className : classNameList) {
readForName(className.getName());
}
Создание экземпляров из универсальных типов во время выполнения
Я не совсем понимаю, чего вы пытаетесь достичь, но на первый взгляд кажется, что самое простое решение - лучшее решение.
Эту проблему можно решить с помощью среды сценариев ( Groovy, JavaScript, JRuby, Jython), которая может динамически оценивать и выполнять произвольный код для создания объектов, но это становится чрезвычайно сложным и чрезмерно сложным, просто для создания объекта.
Но, к сожалению, я думаю, что это очень пешеходное решение.
Пока существует предопределенный набор поддерживаемых типов, вы можете использовать Factory
шаблон. Здесь я просто использую Provider<>T
интерфейс от javax.inject
/com.google.inject
пакет.
Q26289147_ProviderPattern.java
public class Q26289147_ProviderPattern
{
private static final List<String> CLASS_NAMES = ImmutableList.of("String", "Integer", "Boolean");
private static final Map<String, Provider<StrawManParameterizedClass>> PROVIDERS;
static
{
final ImmutableMap.Builder<String, Provider<StrawManParameterizedClass>> imb = ImmutableMap.builder();
for (final String cn : CLASS_NAMES)
{
switch (cn)
{
case "String":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
@Override
public StrawManParameterizedClass<String> get() { return new StrawManParameterizedClass<String>() {}; }
});
break;
case "Integer":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
@Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
case "Boolean":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
@Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
default:
throw new IllegalArgumentException(String.format("%s is not a supported type %s", cn, Joiner.on(",").join(CLASS_NAMES)));
}
}
PROVIDERS = imb.build();
}
static <T> void read(@Nonnull final StrawManParameterizedClass<T> smpc) { System.out.println(smpc.type.toString()); }
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
@Override
public String toString() { return type.getRawType().getCanonicalName(); }
}
public static void main(final String[] args)
{
for (final String cn : CLASS_NAMES)
{
read(PROVIDERS.get(cn).get());
}
}
}
Отказ от ответственности:
Это просто пример концепции, я бы никогда не использовал
switch
Заявление подобное в производственном коде я бы использовалStrategy Pattern
или жеChain of Responsibility
Шаблон для инкапсуляции логики того, какой тип создавать на основеClassName
ключ.
Изначально это выглядело как общая проблема, это не проблема создания.
Тем не менее, вам не нужно передавать случаи Class<?>
ты можешь получить Generic Type
информация о параметризованных классах во время выполнения с TypeToken
из гуавы.
Вы даже можете создавать экземпляры любого универсального типа во время выполнения с TypeToken
из библиотеки гуавы.
Основная проблема в том, что этот синтаксис не поддерживается: Geography<myClass.newInstance()> geo;
и я не могу придумать, чтобы подделать его, кроме Provider
реализация выше.
Вот пример соломенного человека, как использовать
TypeToken
так что ваши параметризованные классы всегда будут знать их типы!
Q26289147.java
import com.google.common.reflect.TypeToken;
public class Q26289147
{
public static void main(final String[] args) throws IllegalAccessException, InstantiationException
{
final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
final String string = (String) smpc.type.getRawType().newInstance();
System.out.format("string = \"%s\"",string);
}
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
}
Заметки:
- Прекрасно работает с классами, у которых по умолчанию нет конструктора arg.
- Работает лучше, чем прямое отражение, если по умолчанию нет конструкторов arg.
- Должен хорошо играть с Guice, позволяя вам использовать ".getRawType()
generated
Учебный классto pass to
getInstance()`Инжектора. еще не пробовал, я просто подумал об этом! - Ты можешь использовать
Class<T>.cast()
делать кастинг, который не нужен@SuppressWarning("unchecked")
повсюду
Вы можете создать статический метод фабрики в Geography (или в любом другом классе):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
Я предположил конструктор класса Geography. Если я угадала неправильно, отредактируйте ваш вопрос, чтобы включить конструктор (и) в географию.
Вы можете создать статический метод фабрики в Geography (или в любом другом классе):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
Я предположил конструктор класса Geography. Если я угадала неправильно, отредактируйте ваш вопрос, чтобы включить конструктор (и) в географию.
Обновление: я не уверен, для чего предназначен класс Geography. Если ему нужен типизированный объект, он может выглядеть так:
public class Geography<T> {
private final T data;
public Geography(T data) {
this.data = Objects.requireNonNull(data);
}
}
Если ему нужен класс, конструктор может выглядеть так:
public class Geography<T> {
private final Class<T> dataClass;
public Geography(Class<T> cls) {
this.dataClass = Objects.requireNonNull(cls);
}
}