【问题标题】:Generic type invocation using string class names使用字符串类名的泛型类型调用
【发布时间】:2014-10-09 22:43:10
【问题描述】:

希望您能帮我解决这个问题: 我有...

  • 一个名为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) 进行初始化。我的输入目录中有 shapefile,其中包含它们的特征所代表的类的名称。我希望 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;
    }

如何摆脱开关?虽然他们的数量是有限的,但是有很多不同的代理......

【问题讨论】:

  • 由于类型擦除,这可能是不可能的。在运行时,只存在一种类型Geography,所有出现的T 都替换为其上限,在您的情况下为Object

标签: java generics shapefile geotools repast-simphony


【解决方案1】:

不幸的是,没有符合您意图的语法(不过是个好主意)。

基本问题是Class.forName() 返回一个未知的Class&lt;?&gt;,所以你需要一个演员某处。这只是你把它放在哪里的问题。

我建议这种方法(编译)基于类名捆绑执行 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());
}

【讨论】:

  • 也可以使用Geography&lt;T&gt; geo = new Geography&lt;&gt;();。我也认为Java中的foreach循环被称为增强的for循环。
  • official documentation 和通俗里称为“for-each 循环”
【解决方案2】:

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

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

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

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

只要有一组预定义的受支持类型,您就可以使用Factory 模式。这里我只是利用javax.inject/com.google.inject 包中的Provider&lt;&gt;T 接口。

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 PatternChain of Responsibility 模式来封装逻辑 根据ClassName 键创建什么类型。

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

也就是说,您不需要传递 Class&lt;?&gt; 的实例,您可以在运行时使用来自 Guava 的 TypeToken 从参数化类中获取 Generic Type 信息。

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

主要问题是不支持这种语法:Geography&lt;myClass.newInstance()&gt; 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()generatedClassto pass togetInstance()`。还没有尝试过,我只是想到了它!时间>
  4. 您可以使用Class&lt;T&gt;.cast() 进行不需要@SuppressWarning("unchecked") 的强制转换。`

【讨论】:

    【解决方案3】:

    您可以在 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);
        }
    }
    

    【讨论】:

    • 感谢您的回答。实际上 Geography 类的构造函数中没有参数化构造函数。标准构造函数不做任何事情。构造函数应该是什么样子,以便我可以使用 Class 来指定 T?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多