在Java中,如何在泛型类型的实例上调用getClass时避免使用原始类型? [英] In Java, how can I avoid raw types when calling getClass on an instance of a generic type?

查看:438
本文介绍了在Java中,如何在泛型类型的实例上调用getClass时避免使用原始类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我在Java中有这个:

  List< String> list = new ArrayList< String>(); 
list.getClass();

最后一个表达式的类型是 Class< ;?扩展List> 。我明白为什么,由于删除,它不能是 Class <?扩展List< String>> 。但为什么它不是 Class <?扩展List<>>



我没有办法避免未检查的投射警告原始类型警告如果我想将此表达式的结果赋值给一个变量,该变量以某种方式保存该类实际上是某种类型的 List ?

  Class <?扩展列表> listClass = list.getClass(); //原始类型警告
Class <?扩展List<>>> listClass =(Class< ;? extends List<>)list.getClass(); // unchecked cast warning


解决方案

当第一次引入泛型时, getClass 返回 Class <?扩展X> ,其中 X 是调用它的表达式的静态类型。这种行为导致了不合理的编译问题,如中所报告的。这是错误报告的例子:

lockquote

以下程序片段无法编译



<$ ($ lt; getClass()== ls.getClass())
; pre $ void f(List }

因为 Class< List< Integer> >
Class< List< String>> 为空。


通过将 getClass 的返回类型扩展为现在的状态,解决了此问题。从文档


实际的结果类型是 Class< ;?其中 | X | 是表达式的静态类型的擦除,其中 getClass 被调用。


解决了上述问题,但最终导致问题出现。不久之后,报道了另一个bug ,主张如下:


我认为 getClass()键入规则可以更改为 Class< ;?扩展
wildcard(T)>



通配符操作定义如下:if T 参数化,
通配符(T)=擦除(T)<> > wildcard(T)= T



JUSTIFICATION:


  1. 这条规则引入了一个原始类型。原始类型只能用于与旧版代码进行交互。

  2. 新规则引入了一个通配符。参数化类型和通配符之间的关系是基于子类型规则的。参数化类型和通配符之间的关系基于原始类型转换。



    这个bug没有采取行动,并且至今仍然开放,并提出以下反驳:


    该提案意味着 getClass( )会返回一个 Class< ;?扩展
    ArrayList<?>>< / code>对象,这与其他类不兼容< ;?扩展
    ArrayList<?>>< / code>对象。这与现有的代码兼容,如:

      List< String> l = ...; 
    Class <?扩展列表> c = l.getClass();

    因为新类型的RHS, Class <? extends List<>> ,是
    类的一个子类型?扩展List>



    丰富Class类型参数的一个缺点是它会
    中断习惯性地使用 Class.cast 。今天,你可以写:

     列表< Integer> x = ...; 
    Class <?扩展列表> cl = x.getClass();
    列表<整数> y = cl.cast(null);

    并在 cast() ,因为未经检查的从 List 转换为 List 。但是对于这个建议,类似的代码不能编译:

      List< Integer> x = ...; 
    Class <?扩展List<>>> cl = x.getClass();
    列表<整数> y = cl.cast(null);

    因为 List<?> cast()无法转换
    List< Integer> 。避免错误的唯一方法是将
    cl.cast(..)转换为 List 并受到
    List< Integer> 的未检查转换警告。这实际上已经是 getClass()了。



    总的来说,这个建议似乎是一个好主意,但它

    (删节并修正了一些错别字)
    $ b复杂度和相当小的回报$ b

    Suppose I have this in Java:

    List<String> list = new ArrayList<String>();
    list.getClass();
    

    The type of the last expression is Class<? extends List>. I understand why, due to erasure, it cannot be Class<? extends List<String>>. But why can't it be Class<? extends List<?>>?

    Is there no way for me to avoid both unchecked cast warnings and raw type warnings if I want to assign the result of this expression to a variable that somehow keeps the information that this class is actually some kind of List?

    Class<? extends List> listClass = list.getClass(); // raw type warning
    Class<? extends List<?>> listClass = (Class<? extends List<?>>) list.getClass(); // unchecked cast warning
    

    解决方案

    When generics were first introduced, getClass returned Class<? extends X>, where X was the static type of the expression on which it was called. This behavior led to unreasonable compilation issues, as reported in this Oracle bug. Here is that bug report's example:

    The following program fragment fails to compile

    void f(List<Integer> li, List<String> ls) {
        if (li.getClass() == ls.getClass())
      ;
    }
    

    because the intersection of Class<List<Integer>> and Class<List<String>> is empty.

    This issue was resolved by widening the return type of getClass to be what it is now. From the documentation:

    The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.

    This resolved the above issue but consequently led to the issue that your question points out. Not long after, another bug was reported, arguing the following:

    I think the getClass() typing rule could be changed to Class<? extends wildcard(T)>

    The wildcard operation is defined by: if T is parametrized, wildcard(T)=erasure(T)<?> else , wildcard(T)=T

    JUSTIFICATION :

    1. This rule introduce a raw type. Raw type must ONLY be used to interact with legacy code.

    2. The new Rule introduce a wildcard. Relationship between parametrized type and wildcard are based on subtyping rules. Relationship between parametrized type and wildcard are based on raw type conversion.

    This bug was not acted upon and remains open to this day, with the following counterarguments:

    The proposal means that getClass() would return a Class<? extends ArrayList<?>> object, which is incompatible with other Class<? extends ArrayList<?>> objects. This is compatible with existing code like:

    List<String> l = ...;
    Class<? extends List> c = l.getClass();
    

    because the new type of the RHS, Class<? extends List<?>>, is a subtype of Class<? extends List>.

    A disadvantage of enriching Class's type argument is that it will break idiomatic use of Class.cast. Today, you can write:

    List<Integer> x = ...;
    Class<? extends List> cl = x.getClass();  
    List<Integer> y = cl.cast(null);
    

    and get a warning at cast(), because of the unchecked conversion from List to List<Integer>. But with the proposal, the analogous code doesn't compile:

    List<Integer> x = ...;
    Class<? extends List<?>> cl = x.getClass();
    List<Integer> y = cl.cast(null);
    

    because List<?> returned by cast() cannot be converted to List<Integer>. The only way to avoid the error is to cast cl.cast(..) up to List and suffer the unchecked conversion warning to List<Integer>. This is effectively what getClass() does already.

    Overall, the proposal seems like a good idea, but it has moderate complexity and a fairly small payoff.

    (abridged and with some typos corrected)

    这篇关于在Java中,如何在泛型类型的实例上调用getClass时避免使用原始类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆