2

希望你能帮助我:我有...

  • 一个类名的字符串列表,称为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 读取 shapefile 并创建相应的代理类。(代理也放置在上下文和地理中。)使用的类有:ShapefileLoaderGeography,其他类可以在同一网站上找到

这部分在main-method中:

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;
    }

我怎样才能摆脱开关?虽然他们的数量是有限的,但有很多不同的代理......

4

3 回答 3

1

在运行时从泛型类型创建实例

我并不完全清楚您要完成的工作,但乍一看,最简单的解决方案似乎是最好的解决方案。

它可以通过使用脚本环境(Groovy、JavaScript、JRuby、Jython)来解决,该环境可以动态评估和执行任意代码来创建对象,但这变得非常复杂和过于复杂,只是为了创建一个对象。

但不幸的是,我认为它有一个非常普通的解决方案。

只要有一组预定义的受支持类型,就可以使用Factory模式。这里我只是利用/包中的Provider<>T接口。javax.injectcom.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 在生产代码中使用这样的语句,我会使用 aStrategy PatternChain of ResponsibilityPattern 来封装基于ClassName密钥创建什么类型的逻辑。

这最初看起来像一个泛型问题,它不是,它是一个创建问题。

也就是说,您不需要传递实例,Class<?>您可以在运行时从 Guava获取Generic Type参数化类的信息。TypeToken

您甚至可以在运行时使用TypeTokenGuava 库创建任何泛型类型的实例。

主要问题是不支持这种语法:除了上面的实现之外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()) {};
    }
}

笔记:

  1. 适用于具有默认无参数构造函数的类。
  2. 如果没有默认的 no arg 构造函数,则比使用直接反射效果更好。
  3. 应该可以很好地与 Guice 一起使用,允许您使用 Injector 的“.getRawType()generatedto pass togetInstance()`。还没有尝试过,我只是想到了它!
  4. 你可以用Class<T>.cast()它来做不需要@SuppressWarning("unchecked")到处的铸造。`
于 2014-10-10T02:41:28.443 回答
1

不幸的是,没有语法接近你的意图(虽然好主意)。

基本问题是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());
}
于 2014-10-10T02:23:53.330 回答
0

您可以在 Geography(或任何其他类)中创建静态工厂方法:

public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
    return new Geography<T>(cls.newInstance());
}

我猜测了 Geography 类的构造函数。如果我猜错了,请编辑您的问题以在 Geography 中包含构造函数。

您可以在 Geography(或任何其他类)中创建静态工厂方法:

public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
    return new Geography<T>(cls.newInstance());
}

我猜测了 Geography 类的构造函数。如果我猜错了,请编辑您的问题以在 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);
    }
}
于 2014-10-09T23:10:39.620 回答