Home  >  Article  >  Java  >  Detailed explanation of Java generics

Detailed explanation of Java generics

高洛峰
高洛峰Original
2016-12-19 14:58:461217browse
  1. Overview
    Before the introduction of generics, Java types were divided into primitive types and complex types, of which complex types were divided into arrays and classes. After the introduction of generics, a complex type can be subdivided into more types.
    For example, the original type List is now subdivided into List, List and other types.
    Note that now List and List are two different types,
    There is no inheritance relationship between them, even if String inherits Object. The following code is illegal
    List ls = new ArrayList();
    List lo = ls;
    The reason for this design is that according to the declaration of lo, the compiler allows you to add to lo Any object (such as Integer), but this object is
    List, which breaks the integrity of the data type.
    Before the introduction of generics, if the method in the class supports multiple data types, the method needs to be overloaded. After the introduction of generics, this problem can be solved
    (polymorphism), and furthermore, multiple parameters can be defined and the relationship between return values.
    For example,
    public void write(Integer i, Integer[] ia);
    public void write(Double d, Double[] da);
    The generic version is
    public void write(T t, T[ ] ta);

    2. Define & use
    The naming style of type parameters is:
    It is recommended that you use concise names as the names of formal type parameters (if possible, a single character). It is best to avoid lowercase letters, which makes it easier to distinguish it from other ordinary
    formal parameters.
    Use T for type, whenever there is no more specific type to distinguish it. This is often seen with generic methods. If there are multiple type parameters, we might use a letter close to T in the alphabet, such as S.
    If a generic function appears in a generic class, it is best to avoid using the same name in the method's type parameters and the class's type parameters to avoid confusion
    . The same goes for inner classes.

    2.1 Define a class with type parameters
    When defining a class with type parameters, in the <> immediately following the class name, specify the name of one or more type parameters, and you can also specify the type parameters.
    The value range is limited, and multiple type parameters are separated by a, sign.
    After defining the type parameters, you can use the type parameters almost anywhere in the class after the definition position (except static blocks, static properties, and static methods),
    just like using ordinary types.
    Note that the type parameters defined by the parent class cannot be inherited by the subclass.
    public class TestClassDefine < ;>, specify the name of one or more type parameters,
    You can also limit the value range of the type parameters, and separate multiple type parameters with a, sign.
    After defining the type parameters, you can use the type parameters anywhere in the method after the definition position, just like using ordinary types.
    For example:
    public T testGenericMethodDefine(T t, S s){
    ...
    }
    Note: The main purpose of defining a method with type parameters is to express multiple parameters and return relationship between values. For example, in the inheritance relationship between T and S in this example, the type of the return value is the same as the value of the first type parameter.
    If you just want to achieve polymorphism, please use wildcards first. See the following section for wildcard content.
    public void testGenericMethodDefine2(List s){
     …
    }
    should be changed to
    public void testGenericMethodDefine2(List s){
     …
    }

    3. Type Parameter assignment
    When assigning values ​​to type parameters of a class or method, all type parameters are required to be assigned. Otherwise, you will get a compilation error.

    3.1 Assigning type parameters to a class with type parameters
    There are two ways to assign type parameters to a class with type parameters
    First, declare a class variable or instantiate it. For example,
    List list;
    list = new ArrayList;
    When the second inherited class or implements the interface. For example
    public class MyList extends ArrayList implements List {...}

    3.2 Assigning values ​​to methods with type parameters
    When calling a generic method, the compiler automatically assigns values ​​to the type parameters. A compilation error occurs when the assignment cannot be successful. For example
    public T testGenericMethodDefine3(T t, List list){
    ...
    }
    public T testGenericMethodDefine4(List list1, List list2){
    ...
    }

    Number n = null;
    Integer i = null;
    Object o = null;
    testGenericMethodDefine(n, i);//At this time T is Number and S is Integer
    testGenericMethodDefine(o, i);// T is Object, S is Integer

    List list1 = null;
    testGenericMethodDefine3(i, list1)//T is Number at this time

    List list2 = null;
    testGenericMethodDefine4(list1, list2)// Compilation error

    3.3 Wildcard
    In the above two sections, specific values ​​are assigned to type parameters. In addition, undefined values ​​can also be assigned to type parameters. For example
    List unknownList;
    List unknownNumberList;
    List unknownBaseLineIntgerList;
    Note: In the Java collection framework, for container classes whose parameter values ​​are of unknown types, they can only be read Among the elements, elements cannot be added to it,
    Because its type is unknown, the compiler cannot identify whether the type of the added element is compatible with the type of the container. The only exception is NULL

    List listString;
    List unknownList2 = listString;
    unknownList = unknownList2;
    listString = unknownList;//Compilation error

    4. Array generic
    You can use a class with generic parameter values ​​to declare an array, but you cannot create an array
    List [] iListArray;
    new ArrayList[10];//Compile time error

    5. Implementation principle

    5.1. Java generic compile-time technology does not contain generic information at runtime, only instances of Class Contains the definition information of type parameters.
    Generics are implemented through a front-end process called erasure by the Java compiler. You can (basically) think of it as a source-to-source transformation, which converts the generic version into the non-generic version.
    Basically, erasure removes all generic type information. All type information between angle brackets is thrown away, so, for example, a
    List type is converted to List. All references to type variables are replaced by the upper bound of the type variable (usually Object). Also,
    Whenever the resulting code type is incorrect, a conversion to the appropriate type will be inserted.
                                                                                                                                                                                                                  . This means they don't add any time or space overhead, which is nice. Unfortunately, this also means that you can't rely on them for type conversion.

    5.2. A generic class is shared by all its calls
    What is the result printed by the following code?
    List l1 = new ArrayList();
    List l2 = new ArrayList();
    System.out.println(l1.getClass() == l2.getClass()) ;
    Maybe you will say false, but you are wrong. It prints true. Because all instances of a generic class have the same runtime class at runtime, regardless of their actual type parameters.
    In fact, the reason why generics are called generics is because they have the same behavior for all its possible type parameters; the same class can be regarded as many different
    types. As a result, the static variables and methods of the class are also shared among all instances. This is why it is illegal to use type parameters (type parameters belong to specific instances) in static methods or static initialization code, or when declaring and initializing static variables.

    5.3. Casting and instanceof
    Another implication of a generic class being shared by all its instances is that it makes no sense to check whether an instance is a specific type of generic class. I Collection CS = New ArrayList & LT; String & GT; ();
    If (cs instanceofllection & lt; string & gt;) {...} // illegal
    法Collection & LT ; String & gt; cstr = (Collection & LT; String & GT; ) cs;
    Get an unchecked warning because the runtime environment will not do such a check for you.

    6. Generic processing of Class
    After Java 5, Class becomes generic.
    One of the changes in JDK1.5 is that the class java.lang.Class is generic. This is an interesting example of extending generics beyond container classes.
    Now, Class has a type parameter T. You may ask, what does T stand for? It represents the type represented by the Class object. For example, the
    String.class type represents Class, and Serializable.class represents Class.
    This can be used to improve the type safety of your reflected code.
    Specially, because Class's newInstance() method now returns a T, you can get a more precise type when creating objects using reflection.
    For example, suppose you want to write a tool method to perform a database query, given a SQL statement, and return a collection of objects in the database that meet the query conditions
    .
    One method is to explicitly pass a factory object, like the following code:
    interface Factory {
       public T[] make();
    }
    public Collection select(Factory   Collection result = new ArrayList();
                                                                                                                                              use using ’ ’ s ’ use ’ s ’ using jdbc ’ t ’s ’ ‐ ‐ ‐ ‐ dbc */                                          Over JDBC Results */
    t item = Factory.make (); Return result;
    }
    You can Call like this:
    select(new Factory(){
      public EmpInfo make() {
        return new EmpInfo();
        }
        } , “selection string”);
    You can also declare a class EmpInfoFactory to support interface F actory :
    class EmpInfoFactory implements Factory { ...
      public EmpInfo make() { return new EmpInfo();}
    }
    Then call:
    select(getMyEmpInfoFactory(), "selection string");
    This solution The disadvantage is that it requires one of two things: a lengthy anonymous factory class at the call site, or declaring a factory class for each type to be used and passing its object to the call site, which is unnatural.
    Using class type parameter values ​​is very natural and can be used by reflection. Code without generics may be:
    Collection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”); ...
    public static Collection select(Class c, String sqlStatement) {
    Collection result = new ArrayList ();
    /* run sql query using jdbc */
    for ( /* iterate over jdbc results */ ) {
    Object item = c.newInstance();
    /* use reflection and set all of item's fields from sql results +/ Now that Class is generic, we can write:
    Collection emps=sqlUtility.select(EmpInfo.class, ”select * from emps”); ...
    public static Collection select( Classc, String sqlStatement) {
    Collection result = new ArrayList();
    /* run sql query using jdbc */
    for ( /* iterate over jdbc results */ ) {
            T item = c.newInstance(); The collection we want.
    This technique is a very useful trick that has become a widely used idiom in new APIs that handle annotations.

    7. Compatibility of old and new code

    7.1. In order to ensure code compatibility, the following code compiler (javac) allows, type safety is guaranteed by yourself
    List l = new ArrayList();
    List< String> l = new ArrayList();

    7.2. When upgrading your class library to a generic version, use covariant return values ​​with caution.
    For example, the code
    public class Foo {
      public Foo create(){
                                                                    use           using                 using                   use                 use                 use      public Foo create(){
                                                                                                                                """"""" ;
    }
    }
    Be careful with the clients of your library.




    For more articles related to detailed explanation of Java generics, please pay attention to the PHP Chinese website!


    Statement:
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn