简体   繁体   English

java 类型擦除如何处理'?

[英]How does java type erasure treats '?'

Hopefully, after understanding generic boundaries, i'm trying to understand wildcard upper and lower bounds.希望在了解通用边界之后,我正在尝试了解通配符上限和下限。 my reference is: https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html I found there a sentence i can understand: "The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; " Fields and local variable?我的参考是: https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html的通配符类型可以在各种情况下使用:“我发现的通配符类型可以在多种情况下使用”参数、字段或局部变量; " 字段和局部变量? can't imagine it.无法想象。 Why such an important source doesn't emphasize it by a simple example?为什么如此重要的资料没有通过一个简单的例子来强调它?

I'm trying to understand with which reference java compiler replaces (while erasure) '?'.我试图了解 java 编译器用哪个参考替换(擦除时)'?'。 Maybe i have a big misunderstanding and any erasure happens (So all the following examples are not relevant).也许我有一个很大的误解并且发生了任何擦除(因此以下所有示例都不相关)。 In the following examples: 1.在以下示例中: 1。

public static void funcA (List<? extends Number>l)

2. 2.

public static void funcB(List<? super Integer>l)

and is there a difference between second example and the following code: 3.第二个示例与以下代码之间是否有区别:3。

public static <T extends Integer> funcC(List<? extends T>l)

And if there any different between example 2 and the following one: 4.如果示例 2 与以下示例有任何不同:4。

public static <T extends Integer> void funcC(List<T>l)

Preface: You've asked a number of questions about type erasure, including in the chat room.前言:你问了很多关于类型擦除的问题,包括在聊天室里。 While this answer will address this specific question it may answer some of your other questions as well (maybe even some you haven't asked yet).虽然此答案将解决此特定问题,但它也可能会回答您的其他一些问题(甚至可能是您尚未问过的一些问题)。

Warning: This answer is long and the question specifically asked in this post is only directly addressed at the end.警告:这个答案很长,这篇文章中特别提出的问题只在最后直接解决。


What is type erasure?什么是类型擦除?

This has been covered quite well in other Q&As so I will simply link to them:这在其他问答中已经很好地涵盖了,所以我将简单地链接到它们:


What are the rules of type erasure?类型擦除的规则是什么?

The rules of type erasure are specified in §4.6 Type Erasure of the Java Language Specification (JLS) :类型擦除的规则在Java 语言规范 (JLS)§4.6 类型擦除中指定:

Type erasure is a mapping from types (possibly including parameterized types and type variables) to types (that are never parameterized types or type variables).类型擦除是从类型(可能包括参数化类型和类型变量)到类型(从不参数化类型或类型变量)的映射。 We write |T|我们写|T| for the erasure of type T .用于擦除类型T The erasure mapping is defined as follows:擦除映射定义如下:

  • The erasure of a parameterized type ( §4.5 ) G<T1,...,Tn> is |G|参数化类型 ( §4.5 ) G<T1,...,Tn>的擦除是|G| . .

  • The erasure of a nested type T.C is |T|.C .嵌套类型T.C的擦除是|T|.C

  • The erasure of an array type T[] is |T|[] .数组类型T[]的擦除是|T|[]

  • The erasure of a type variable ( §4.4 ) is the erasure of its leftmost bound.类型变量的擦除( 第 4.4 节)是其最左边界的擦除。

  • The erasure of every other type is the type itself.所有其他类型的擦除都是类型本身。

Type erasure also maps the signature ( §8.4.2 ) of a constructor or method to a signature that has no parameterized types or type variables.类型擦除还将构造函数或方法的签名( 第 8.4.2 节)映射到没有参数化类型或类型变量的签名。 The erasure of a constructor or method signature s is a signature consisting of the same name as s and the erasures of all the formal parameter types given in s .构造函数或方法签名s的擦除是由与 s 相同的名称和s中给出s所有形式参数类型的擦除组成的签名。

The return type of a method ( §8.4.5 ) and the type parameters of a generic method or constructor ( §8.4.4 , §8.8.4 ) also undergo erasure if the method or constructor's signature is erased.如果方法或构造函数的签名被擦除,方法的返回类型( §8.4.5 )和泛型方法或构造函数的类型参数( §8.4.4§8.8.4 )也会被擦除。

The erasure of the signature of a generic method has no type parameters.泛型方法签名的擦除没有类型参数。

For this answer we should focus on the first and fourth bullet points which say:对于这个答案,我们应该关注第一个和第四个要点:

The erasure of a parameterized type ( §4.5 ) G<T1,...,Tn> is |G|参数化类型 ( §4.5 ) G<T1,...,Tn>的擦除是|G| . .

And:和:

The erasure of a type variable ( §4.4 ) is the erasure of its leftmost bound.类型变量的擦除( 第 4.4 节)是其最左边界的擦除。

Respectively.分别。 In particular, notice how the fourth bullet point only explains the erasure of type variables .特别要注意第四个要点如何只解释类型变量的擦除。 Why does this matter?为什么这很重要? I'll come back to that.我会回到那个。

Terminology术语

Understanding the terms is important to understanding how the rules are applied:理解这些术语对于理解如何应用规则很重要:

  • Generic class通用 class

  • Generic interface通用接口

  • Generic method通用方法

    • Specified in §8.4.4 Generic Methods of the JLS.在 JLS 的§8.4.4 通用方法中指定。
    • A generic method is a method which declares one or more type variables .泛型方法是一种声明一个或多个类型变量的方法。
  • Generic constructor通用构造函数

  • Type variable类型变量

    • Specified in §4.4 Type Variables of the JLS.在 JLS 的§4.4 类型变量中指定。
    • A type variable is introduced by a type parameter declared on the generic member.类型变量由在泛型成员上声明的类型参数引入。
    • The syntax of a type variable is: {Annotation} TypeIdentifier类型变量语法是: {Annotation} TypeIdentifier
  • Type parameter类型参数

    • Specified in many sections of the JLS.在 JLS 的许多部分中指定。 Every section I linked to for the above terms mentions type parameters .我为上述术语链接的每个部分都提到了类型参数
    • A type parameter is the declaration of a type variable , plus its bounds, on a generic member.类型参数是泛型成员上类型变量的声明及其边界。
    • The syntax of a type parameter is: {TypeParameterModifier} TypeIdentifier [TypeBound] where TypeParameterModifier expands into Annotation .类型参数语法是: {TypeParameterModifier} TypeIdentifier [TypeBound]其中TypeParameterModifier扩展为Annotation
  • Parameterized type参数化类型

    • Specified in §4.5 Parameterized Types of the JLS.§4.5 JLS 的参数化类型中指定。
    • A parameterized type is the actual use of a generic class or generic interface with type arguments .参数化类型通用 class类型为 arguments通用接口的实际使用。
  • Type argument类型参数

Note that the difference between type parameter and type argument is similar to how Java differentiates between a method parameter and argument.请注意,类型参数类型参数之间的区别类似于 Java 如何区分方法参数和参数。 When you declare a method as void bar(Object obj) the Object obj is a parameter.当您将方法声明为void bar(Object obj)时, Object obj是一个参数。 However, when you call the method like bar(someObjInstance) the value of someObjInstance is the argument.但是,当您调用bar(someObjInstance)之类的方法时, someObjInstance的值就是参数。

Code Examples of the Terminology术语的代码示例

Seeing some examples in code can help understand what parts of the code each term applies to.查看代码中的一些示例可以帮助理解每个术语适用于代码的哪些部分。

A generic class with type parameters具有类型参数的通用 class

public class Foo<T extends CharSequence, U> {
    // class body...
}

There are two type parameters :有两种类型参数

  1. T extends Charsequence

    • The type variable is T .类型变量T
    • The type bounds are extends CharSequence类型边界extends CharSequence
  2. U

    • The type variable is U类型变量U
    • The type bounds are extends Object (implicitly defined)类型边界extends Object (隐式定义)

The code looks similar for generic interfaces.通用接口的代码看起来很相似。

A generic method with a type parameter带有类型参数的泛型方法

public void <V extends Number> bar(V obj) {
   // method body...
}

This method has one type parameter :此方法有一个类型参数

  1. V extends Number
    • The type variable is V .类型变量V
    • The type bounds are extends Number .类型边界extends Number

The code looks similar for generic constructors.对于泛型构造函数,代码看起来很相似。

A parameterized type (no wildcard)参数化类型(无通配符)

public <E extends Number> void bar(List<E> list) {
    // method body...
}

There is one parameterized type :有一种参数化类型

  1. List<E>
    • The type argument is E .类型参数E

A parameterized type (with wildcard)参数化类型(带通配符)

public void bar(List<? extends Number> list) {
    // method body...
}

There is one parameterized type :有一种参数化类型

  1. List<? extends Number>
    • The type argument is ? extends Number类型参数? extends Number ? extends Number

Back to the Rules回到规则

As I mentioned, it's important to note that the rules only mention the erasure of type variables .正如我所提到的,重要的是要注意规则只提到了类型变量的擦除。 The reason this is important is because wildcards are not allowed in the places where type variables can be defined (ie in type parameters ).这很重要的原因是因为在可以定义类型变量的地方(即在类型参数中)不允许使用通配符。 A wildcard can only be used in a type argument which is a part of a parameterized type .通配符只能用在作为参数化类型一部分的类型参数中。

The erasure of a parameterized type is simply the raw type.参数化类型的擦除只是原始类型。


When Does Type Erasure Matter?什么时候类型擦除很重要?

In everyday development of generic code type erasure is virtually irrelevant.在通用代码类型擦除的日常开发中几乎是无关紧要的。 One of the only times you have to know how type erasure works in some detail is when working with raw types .在使用原始类型时,您必须详细了解类型擦除是如何工作的唯一一次。 In an ideal and just world you'll only work with raw types when working with legacy code (from the days before Java 5) that you cannot change.在一个理想且公正的世界中,您只能在处理无法更改的遗留代码(从 Java 5 之前的日子)时使用原始类型。 In other words, unless you're forced to work with raw types you should always appropriately use generics.换句话说,除非您被迫使用原始类型,否则您应该始终适当地使用 generics。

However, maybe you're forced to work with raw types or you're simply curious.但是,也许您被迫使用原始类型,或者您只是好奇。 In that case, you need to know how type erasure works because the erasure determines what types are used.在这种情况下,您需要知道类型擦除是如何工作的,因为擦除决定了使用什么类型。 Here's an example of a generic class:下面是一个通用 class 的示例:

public class Foo<T extend CharSequence, U> {

    private List<T> listField;
    private U objField;

    public void bar(List<? extends T> listParam) {
        // method body...
    }

    public U baz(T objParam) {
        // method body...
    }

    public <V extends Number> V qux(V objParam) {
        // method body...
    }

}

And following the aforementioned rules of type erasure, here's what the above class looks like afterwards:遵循上述类型擦除规则,上面的 class 如下所示:

// the raw type of Foo
public class Foo {

    private List listField;
    private Object objField;

    public void bar(List listParam) {
        // method body...
    }

    public Object baz(CharSequence objParam) {
        // method body...
    }

    public Number qux(Number objParam) {
        // method body...
    }

}

But again, you'll only need to know about the latter version when you're using raw types.但同样,当您使用原始类型时,您只需要了解后一个版本。


Applying This Knowledge to Your Question将此知识应用于您的问题

Some of what we've learned so far is that wildcards can only be used in type arguments and are thus only applicable to parameterized types .到目前为止,我们了解到的一些内容是通配符只能用于arguments 类型,因此仅适用于参数化类型 The erasure of a parameterized type is simply the raw type.参数化类型的擦除只是原始类型。 If we apply this knowledge to your examples you get the following:如果我们将这些知识应用于您的示例,您将获得以下信息:

  1. Example #1示例 #1

    • Original原来的

      public static void funcA(List<? extends Number> l)
    • Erased已删除

      public static void funcA(List l)
  2. Example #2示例 #2

    • Original原来的

      public static void funcB(List<? super Integer> l)
    • Erased已删除

      public static void funcB(List l)
  3. Example #3示例#3

    • Original (forgot to specify return type in question, assuming void )原始(忘记指定有问题的返回类型,假设为void

       public static <T extends Integer> void funcC(List<? extends T> l)
    • Erased已删除

      public static void funcC(List l)
  4. Example #4示例 #4

    • Original原来的

      public static <T extends Integer> void funcC(List<T> l)
    • Erased已删除

      public static void funcC(List l)

Reinforcing the Point加强重点

To really point out the difference between the erasure of a type variable and the erasure of a parameterized type let's look at another example.为了真正指出擦除类型变量和擦除参数化类型之间的区别,让我们看另一个示例。

public class Foo<T extends Number> {

    public void bar(T obj) {
        // method body...
    }

    public void baz(List<? extends T> list) {
        // method body...
    }

}

The method bar has a single parameter of type T .方法bar有一个T类型的参数。 The parameter is using the type variable directly.该参数直接使用类型变量 The erasure of a type variable is the erasure of its leftmost bound which is Number in this case.类型变量的擦除是擦除其最左边的边界,在这种情况下是Number This means after erasure the method's parameter is Number .这意味着擦除后方法的参数是Number

The method baz has a single parameter of type List<? extends T>方法baz有一个List<? extends T> List<? extends T> . List<? extends T> Here, the type variable T is being used as the upper bound in the type argument of a parameterized type .在这里,类型变量T被用作参数化类型类型参数的上限。 In other words, despite a type variable being used, the erasure actually being used here is that of a parameterized type .换句话说,尽管使用了类型变量,但这里实际使用的擦除是参数化类型的擦除。 This means after erasure the method's parameter is just List .这意味着在擦除方法的参数后只是List This would happen even if the type argument was a lower-bounded wildcard (eg List<? super T> ), an unbounded wildcard (eg List<?> ), or even a non-wildcard (eg List<T> ).即使类型参数是下界通配符(例如List<? super T> )、无界通配符(例如List<?> )或什至是非通配符(例如List<T> ),也会发生这种情况。

How Wildcards are Handled by Type Erasure类型擦除如何处理通配符

To directly answer your question on how type erasure treats wildcards, the answer is effectively: It doesn't, not directly .要直接回答您关于类型擦除如何处理通配符的问题,答案是有效的:它不是,不是直接的。 Wildcards simply disappear (when the parameterized type is erased) and have no significance in the resulting raw type.通配符只是消失(当参数化类型被删除时)并且在生成的原始类型中没有意义。

Wildcards enable flexibility for those using the generic API.通配符为使用通用 API 的用户提供了灵活性。 Here are some Q&As which address that concept:以下是一些解决该概念的问答:

Hopefully those Q&As also help answer your auxiliary question, which is what are the differences between the second and fourth examples from your post.希望这些问答也有助于回答您的辅助问题,即您帖子中的第二个和第四个示例之间有什么区别。

Example 2 is saying that you can pass List<? super Integer>示例 2 是说你可以通过List<? super Integer> List<? super Integer> , so a List<Integer> is ok, but also List<Number> or List<Object> . List<? super Integer> ,所以List<Integer>是可以的,但List<Number>List<Object>也是可以的。

The next example however says you can pass List<? extends T>然而,下一个例子说你可以通过List<? extends T> List<? extends T> where <T extends Integer> . List<? extends T>其中<T extends Integer> So really you can only pass List<Integer> and if they existed any subclass of Integer (which don't exist because Integer is final).所以真的你只能通过List<Integer>并且如果它们存在Integer的任何子类(不存在,因为Integer是最终的)。

As you can see they are quite the opposite of each other.如您所见,它们彼此完全相反。

The last example is sort of similar.最后一个例子有点类似。 You are not specifically using T anywhere else, so it is resolving to the same thing.您没有在其他任何地方专门使用T ,因此它正在解决同一问题。 This would be more useful if you needed T as an extra parameter or part of the return value.如果您需要T作为额外参数或返回值的一部分,这将更有用。

Finally don't get confused about the role of type erasure.最后不要对类型擦除的作用感到困惑。 The ? ? is a generic type wildcard, nothing related to type erasure.是通用类型通配符,与类型擦除无关。

Type erasure is just the fact that at runtime generic types do not exist during execution.类型擦除只是在运行时泛型类型在执行期间不存在的事实。 Generics only enforce compile time checks. Generics 仅强制执行编译时检查。 After compilation you will just have a plain List.编译后你将有一个简单的列表。

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

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