【问题标题】:How to create a class literal of a known type: Class<List<String>>如何创建已知类型的类文字:Class<List<String>>
【发布时间】:2011-01-02 00:12:48
【问题描述】:

采取以下措施:

public Class<List<String>> getObjectType() {
    // what can I return here?
}

我可以从这个方法返回什么类文字表达式来满足泛型并编译? List.class 不会编译,List.&lt;String&gt;class 也不会。

如果您想知道“为什么”,我正在编写 Spring 的 FactoryBean&lt;List&lt;String&gt;&gt; 的实现,这需要我实现 Class&lt;List&lt;String&gt;&gt; getObjectType()。但是,这不是 Spring 问题。

编辑: SpringSource 的权力已经听到了我的哀求,因此 Spring 3.0.1 将返回类型 getObjectType() 更改为 Class&lt;?&gt;,这巧妙地避免了问题。

【问题讨论】:

    标签: java generics jls


    【解决方案1】:

    你总是可以投射到你需要的东西,像这样

    return (Class<List<String>>) new ArrayList<String>().getClass();
    

    return (Class<List<String>>) Collections.<String>emptyList().getClass();
    

    但我认为这不是你所追求的。好吧,它有效,但有警告,但它并不完全“漂亮”。

    我刚刚发现了这个

    为什么通配符参数化类型没有类文字?

    因为通配符参数化类型没有精确的运行时类型表示。

    所以铸造可能是唯一的方法。

    【讨论】:

    • 你在哪里找到的那​​句话?
    • 您的第一个示例对我不起作用(SE 1.6),只有第二个示例可以。将 ArrayList 实例化为变量并返回 myVar.getClass() 可以。发现:java.lang.Class 必需:java.lang.Class>
    【解决方案2】:

    您可以像这样实现该方法:

    public Class<List<String>> getObjectType() {
        return (Class<List<String>>) ((Class)List.class);
    }
    

    【讨论】:

    • 我喜欢这种方式,因为您不需要创建新实例。
    • 不需要外部演员表。你可以这样写:return (Class)List.class;,我觉得它更容易阅读。
    【解决方案3】:

    下面的做法是有问题的:

    > public Class<List<String>> getModelType() {
    >   return (Class<List<String>>) new ArrayList<String>().getClass();
    > }
    

    例如如果你想测试一个对象是否属于

    org.eclipse.emf.common.util.BasicEList<String> 
    

    是类型

    List<String> 
    

    基于上述getModelType()方法的结果,例如:

    BasicEList<String> fromObject = ...;
    if (getModelType().isAssignableFrom(fromObject.getClass())) {
        transferFromModelToUi(getModelType().cast(fromObject));
    }
    

    它会导致 false 而它应该是 true 因为两个对象都实现了接口 List(因为 getModelType() 返回一个 List 类型的 Class 对象而不是 ArrayList)。

    这是一种对我有用的方法(有点麻烦,但在上面的示例中会产生正确的结果,可以移至静态初始化程序):

    public Class<List<String>> getModelType() {
        Class<?> arrayListClass = new ArrayList<String>().getClass();
        Class<?>[] interfaces = arrayListClass.getInterfaces();
        int index = 0;
        for (int i = 0; i < interfaces.length; i++) {
            if (interfaces[i].equals(List.class)) {
                index = i;
                break;
            }
        }
        return (Class<List<String>>) interfaces[index];
    }
    

    【讨论】:

      【解决方案4】:

      这个呢:

      public class TestMain {
          public static void main(String[] args) throws Exception {
              Type type = TestMain.class.getMethod("dummy").getGenericReturnType();
              System.out.println("type = " + type);
          }
      
          public List<Integer> dummy() {return null;}
      }
      

      这打印:

      type = java.util.List<java.lang.Integer>
      

      【讨论】:

        【解决方案5】:

        您永远不应该使用构造Class&lt;List&lt;String&gt;&gt;。这是荒谬的,应该在 Java 中产生警告(但不会)。类实例总是代表原始类型,所以你可以有Class&lt;List&gt;;而已。如果你想要一些东西来表示像 List&lt;String&gt; 这样的具体泛型类型,你需要一个像 Guice 使用的“超级类型令牌”:

        http://google-guice.googlecode.com/git/javadoc/com/google/inject/TypeLiteral.html

        【讨论】:

        • 我同意这不是一个好主意,但Java允许这样做,如果你试图实现一个接口,你可能没有选择,所以我们仍然需要一个方法它(半)优雅。
        【解决方案6】:

        Class&lt;List&lt;String&gt;&gt; 的存在本质上是危险的。原因如下:

        // This statement generates a warning - for a reason...
        Class<List<String>> unsafeListClass = (Class<List<String>>) (Class<?>) List.class;
        
        List<Integer> integerList = new ArrayList<Integer>(); // Ok
        integerList.add(42); // Ok
        
        System.out.println(unsafeListClass.isInstance(integerList)); // Prints "true".
        List<String> stringList =
           unsafeListClass.cast(integerList); // Succeeds, with no warning!
        stringList.add("Hello, World!"); // Also succeeds with no warning
        
        for (int x: integerList) {
            // Compiles without warning, but throws ClassCastException at runtime
            System.out.println(100-x);
        }
        

        【讨论】:

        • 这是一个奇怪的例子。由于泛型在运行时没有具体化,因此运行时强制转换在许多情况下会绕过泛型类型系统。泛型主要是一种编译时构造,它允许您从类类型中进一步约束类型。如果您可以保留通用签名,那将很有用,因为只要您不强制转换,就会使许多代码更具可读性和类型安全性。
        【解决方案7】:

        查看 SUN 论坛上的讨论:

        http://forums.sun.com/thread.jspa?threadID=5253007

        以及描述使用“超级类型令牌”解决方法的参考博客文章:

        http://gafter.blogspot.com/2006/12/super-type-tokens.html

        【讨论】:

          【解决方案8】:

          在 springframework.org 上找到 this link,这提供了一些见解。

          例如

          List<String> myList = new ArrayList<String>();
          return (Class<List<String>>)myList.getClass();
          

          【讨论】:

          • @Bozho:springframework 链接有不同的方法签名java.lang.Class&lt;? extends T&gt; getObjectType()。我已经更新了我的答案以反映 OP 问题的签名。
          【解决方案9】:

          我不确定这是否可行,因为任何类文字都将被编译为 Class.forName(...),并且由于这发生在运行时,因此没有留下任何通用信息。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-10-19
            • 1970-01-01
            • 2020-12-04
            • 1970-01-01
            • 2021-07-06
            • 2021-02-17
            • 1970-01-01
            • 2020-06-19
            相关资源
            最近更新 更多