Java中的动态通用键入

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (22)

如果我有一个使用泛型类的类,比如

public class Record<T> {
    private T value;

    public Record(T value) {
        this.value = value;
    }
}

如果我知道使用的所有类型,例如在此示例中就是这种情况,那么在设计时输入所有内容都非常简单:

// I type explicitly
String myStr = "A";
Integer myInt = 1;
ArrayList myList = new ArrayList();

Record rec1 = new Record<String>(myStr);
Record rec2 = new Record<Integer>(myInt);
Record rec3 = new Record<ArrayList>(myList);

如果我从不知道类型的“somewhere”获取对象列表,会发生什么情况?我如何分配类型:

// now let's assume that my values come from a list where I only know during runtime what type they have

ArrayList<Object> myObjectList = new ArrayList<Object>();
    myObjectList.add(myStr);
    myObjectList.add(myInt);
    myObjectList.add(myList);

    Object object = myObjectList.get(0);

    // this fails - how do I do that?
    new Record<object.getClass()>(object);
提问于
用户回答回答于

Java泛型不是C ++模板。

Java泛型是编译时功能,而不是运行时功能。

这里是一个Java泛型教程的链接。

这无法用于Java:

new Record<object.getClass()>(object);

你必须使用多态(比如每个对象实现一个已知的接口)或RTTI(instanceof或Class.isAssignableFrom())。

你可能会这样做:

     class Record
     {
       public Record(String blah) { ... }
       public Record(Integer blah) { ... }
       ... other constructors.
     }

或者你可以使用Builder模式

用户回答回答于

在运行时从通用类型创建实例

我不完全清楚你想要完成什么,但首先看起来最简单的解决方案是最好的解决方案。

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

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

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

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 在生产代码中使用类似的语句,我会使用a Strategy PatternChain of ResponsibilityPattern来封装基于ClassName密钥创建的类型的逻辑。

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

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

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

主要的问题是这个语法不被支持:Geography<myClass.newInstance()> geo;并且我不能想到除了Provider上面的实现之外,它是假的。

下面是一个如何使用稻草人示例,TypeToken以便您的参数化类将始终知道它们的类型!

.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. 如果没有默认的无参数构造函数,比使用直接反射效果更好。
  3. 应该和Guice一起玩,让你使用Injector的“.getRawType()generatedto pass togetInstance()` 还没有尝试过,我只是想到了!
  4. 你可以Class<T>.cast()用来做不需要@SuppressWarning("unchecked")遍布整个地方的演员。“

扫码关注云+社区