简体   繁体   English

Java泛型和类型擦除

[英]Java generics and type erasure

Given the following code: 给出以下代码:

public void example(Object o) {
  if(o instanceof List<MyType>)
    //do something
}

I understand that this is not possible (and why its not possible) given the way Java handles generics and type erasure. 我了解,鉴于Java处理泛型和类型擦除的方式,这是不可能的(以及为什么不可能如此)。

My question is, what is the best/cleanest way to accomplish this? 我的问题是,最佳/最干净的方法是什么? Or is the only thing I can do is check if o is a List<?> ? 还是我唯一能做的就是检查o是否为List<?>

You cannot check more than o instanceof List<?> . 您最多只能检查一次o instanceof List<?>

The type safety you get from Java generics happens only at compile-time. 从Java泛型获得的类型安全性仅在编译时发生。 A method like yours (which accepts an Object and then tries to figure out what to do), does not play well with this design. 像您这样的方法(接受一个对象然后尝试找出要做什么),在这种设计中效果不佳。 I can see why such a dynamic method is necessary sometime, but consider supplementing it with versions for typed parameters: 我可以看到为什么有时需要这样的动态方法,但是可以考虑用类型化参数的版本来补充它:

public void example(Object o) {
  if(o instanceof List<?>)
     example((List)o);    // can only hope that the type is correct here
}

public void example(List<MyType> list){
    // do something
}

In the cases where those can be used, you get the full benefit of generics. 在可以使用它们的情况下,您将获得泛型的全部好处。 In other cases, you have to depend on people reading your Javadoc and only passing in the correct types. 在其他情况下,您必须依靠人们阅读Javadoc并仅传递正确的类型。

What even the approach above cannot do is have two different code paths for List<TypeA> and List<TypeB> . 甚至上面的方法也不能做到的是, List<TypeA>List<TypeB>具有两个不同的代码路径。 If this becomes really necessary for you, consider using your own wrapper types ListOfTypeA , ListOfTypeB . 如果确实需ListOfTypeA ,请考虑使用自己的包装器类型ListOfTypeAListOfTypeB

Depending on what you need to do, it may not even be necessary to look at the erased type of the list as a whole, and just work on the runtime types of the individual elements: 根据您需要执行的操作,甚至可能根本没有必要查看列表的已擦除类型,而只需要处理单个元素的运行时类型即可:

 for (Object o: list){
    if (o instanceof TypeA){
    }
    if (o instanceof TypeB){
    }
 }

Short of checking the types of all the items in the List, it is not possible to determine the type of the generic parameter in Java, as this only exists at compile time and is removed prior to runtime. 如果不检查List中所有项目的类型,就无法确定Java中通用参数的类型,因为它仅在编译时存在,并在运行时删除。

My suggestion would be unless you absolutely need to have the method take in an Object (such as conforming to a interface spec or overriding Object.equals) to take in the correct type you want as a parameter to the method and to overload the method with the various other types you might need to run the method with. 我的建议是,除非您绝对需要使该方法接受一个Object(例如符合接口规范或重写Object.equals),然后将所需的正确类型作为该方法的参数,并使用您可能需要使用其他各种类型来运行该方法。

Java erases type information for generics after compilation so you can't check the type parameter in this way dynamically. Java在编译后会删除泛型的类型信息,因此您不能以这种方式动态检查类型参数。

If all paths to this code restrict the type parameter then doing: 如果此代码的所有路径都限制了type参数,请执行以下操作:

// Return true if object is a list of MyType, false if it is not a list, or is 
// empty
boolean isListOfMyType(Object o) {
    if (o instanceof List) {
        List<?> l = (List<?) o;
        return (l.size() > 0 && (l.get(0) instanceof MyType) 
    }
    return true;
}

is typesafe, although it will only work if the list is not empty. 是类型安全的,尽管仅在列表不为空时才起作用。 If not you will need to modify the above to check whether all the items pass the instanceof test (or if they are null if you allow nulls in your list). 如果不是,则需要修改上面的内容以检查所有项目是否都通过了instanceof test(如果列表中允许为null,则它们是否为null)。

Another alternative is to create a subclass that extends say ArrayList<MyType> and use this for your instanceof check. 另一种选择是创建一个扩展了ArrayList<MyType>的子类,并将其用于您的instanceof检查。

Last but not least having a subclass that implements List<MyType> will allow you to get at the type parameter using the Class.getGenericInterfaces() method. 最后但并非最不重要的是,有一个实现List<MyType>的子类将使您可以使用Class.getGenericInterfaces()方法获得type参数。 See this for details. 请参阅了解详情。

For either of these last two methods to work you have to ensure that creation of the list always instantiates one of those types. 对于这两种最后一种方法,您必须确保列表的创建始终实例化这些类型之一。 Ie. 就是 if the caller goes and constructs their own ArrayList<MyType> it will not work. 如果调用方去构建自己的ArrayList<MyType> ,它将无法正常工作。

Contrary to what is widely accepted and rarely known type erasure can be avoided, which means that the callee do have the ability to know which generic parameters were employed during the call. 与被广泛接受且很少为人所知的类型擦除相反,可以避免这种情况,这意味着被调用者确实能够知道在调用过程中使用了哪些通用参数。

Please have a look at: Using TypeTokens to retrieve generic parameters 请看一下: 使用TypeTokens检索通用参数

Thanks 谢谢

Perhaps by checking the object type at runtime, maybe something like: 也许通过在运行时检查对象类型,也许像:

if (o.getClass() == List.class) ...

Obviously, you'll have to dig deeper into the object's class type to see if it's an exact match to List<> and the list's element types. 显然,您必须更深入地研究对象的类类型,以查看它是否与List<>和列表的元素类型完全匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM