Java programming manual-generic

Source: Internet
Author: User

Java programming manual-generic
1. Introduction of generics (JDK 1.5)
You must be familiar with passing in a parameter in a method. The general practice is to put the parameter in a parentheses () and pass them to the method. In generics, we can pass the type information like passing parameters in the method, by placing the type in a angle bracket <>.

JDK 1.5 introduces generics, which allow us to parameterize the types, that is, the class designer uses generics in the class definition process, in this way, the user can dynamically specify the type during class instantiation or method calling, so that the type can be dynamically used within the class.

For example, the ArrayList class is designed as a generic type by the designer. It has a generic type. .

 

public class ArrayList
 
   implements List
  
    .... {   // Constructor   public ArraList() { ...... }   // Public methods   public boolean add(E e) { ...... }   public void add(int index, E element) { ...... }   public boolean addAll(int index, Collection
    c)   public abstract E get(int index) { ...... }   public E remove(int index)   .......}
  
 

When instantiating an ArrayList, the user needs to specify a specific type for E, which will replace all E in the class, that is, all the places where type E is used in the class are replaced with the actual type we specified.

 

 

ArrayList
 
   lst1 = new ArrayList
  
   (); // E substituted with Integerlst1.add(0, new Integer(88));lst1.get(0); ArrayList
   
     lst2 = new ArrayList
    
     ();   // E substituted with Stringlst2.add(0, "Hello");lst2.get(0);
    
   
  
 

In the preceding example, the class designer uses the generic type when defining the class, so that the user needs to specify a real type for the generic type E when instantiating the class, the transfer of type information is passed through <>, which is similar to the transfer of method parameters through.

The type is insecure before the set is introduced to the generic type.
If you compare the attributes of a collection in versions earlier than JDK 1.5, you should know that the element types inside the set are all java. lang. object, which uses a polymorphism principle, because any Object subclass can be replaced by an Object, and the Object is a superclass of all Object types in Java, in this way, we can store any object type in the collection. However, it has an obvious problem. If we define an ArrayList for storing String objects, our String type will be converted to the Object type during the add (Object) operation, this is an implicit operation by the compiler, but when we get this element, we get an Object of the Object type. At this time, we need to manually explicitly convert an Object type Object to a String type Object. If we convert the Object type to a non-String type Object or add (Object) it stores a non-String object and converts it to the String type when obtaining this element. At this time, the compiler cannot check for errors, however, a ClassCastException is thrown during manual type conversion during running.

 

// Pre-JDK 1.5import java.util.*;public class ArrayListWithoutGenericsTest {   public static void main(String[] args) {      List strLst = new ArrayList();  // List and ArrayList holds Objects      strLst.add("alpha");            // String upcast to Object implicitly      strLst.add("beta");      strLst.add("charlie");      Iterator iter = strLst.iterator();      while (iter.hasNext()) {         String str = (String)iter.next(); // need to explicitly downcast Object back to String         System.out.println(str);      }      strLst.add(new Integer(1234));       // Compiler/runtime cannot detect this error      String str = (String)strLst.get(3);  // compile ok, but runtime ClassCastException   }}
To solve this problem, the type check can be performed during compilation, so that the generic type is introduced.

 

2. Generic

The following is a custom ArrayList version called MyArrayList, which does not use generics.

 

// A dynamically allocated array which holds a collection of java.lang.Object - without genericspublic class MyArrayList {   private int size;     // number of elements   private Object[] elements;      public MyArrayList() {         // constructor      elements = new Object[10];  // allocate initial capacity of 10      size = 0;   }      public void add(Object o) {      if (size < elements.length) {         elements[size] = o;      } else {         // allocate a larger array and add the element, omitted      }      ++size;   }      public Object get(int index) {      if (index >= size)         throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);      return elements[index];   }      public int size() { return size; }}
From the above, we can easily see that MyArrayList is not of type security. For example, if we want to create a MyArrayList that stores String-type objects, but we add an Integer object to it, the compiler cannot check for exceptions because MyArrayList is designed to store elements of the Object type, and any Object type can be converted to objects of the Object type.

 

public class MyArrayListTest {   public static void main(String[] args) {      // Intends to hold a list of Strings, but not type-safe      MyArrayList strLst = new MyArrayList();      // adding String elements - implicitly upcast to Object      strLst.add("alpha");      strLst.add("beta");      // retrieving - need to explicitly downcast back to String      for (int i = 0; i < strLst.size(); ++i) {         String str = (String)strLst.get(i);         System.out.println(str);      }         // Inadvertently added a non-String object will cause a runtime      // ClassCastException. Compiler unable to catch the error.      strLst.add(new Integer(1234));   // compiler/runtime cannot detect this error      for (int i = 0; i < strLst.size(); ++i) {         String str = (String)strLst.get(i);   // compile ok, runtime ClassCastException         System.out.println(str);      }   }}

We can see from the above that if we want to create a String type List, but we have added a non-String type Object element, this Object can also be converted to the Object type, in addition, the compiler automatically completes, And the compiler cannot check whether it is valid. This poses a hidden danger. When we obtain this element, it is of the Object type, we need to manually convert to the String type. In this case, the ClassCastException will be thrown, which occurs during the runtime.

 

2.1 generic classes

JDK 1.5 introduces the so-called generics to solve this problem. Generics allow us to abstract types, we can create a generic class and specify the specific type information during class instantiation. The compiler checks the corresponding types during the compiler. This ensures that no type conversion exception occurs during the runtime. This is called type security.

Next let's take a look at java. util. List .

 

public interface List
 
   extends Collection
  
    {   boolean add(E o);   void add(int index, E element);   boolean addAll(Collection
    c);   boolean containsAll(Collection
    c);   ......}
  
 

It is a formal type parameter. During class instantiation, the actual type parameter can be passed in to replace this formal type parameter.

This is the same as the method call. When defining a method, we declare the form parameter. when calling the method, the form parameter will accept the real parameter.

For example, the method definition declares the form parameter.

 

// A method's definitionpublic static int max(int a, int b) {  // int a, int b are formal parameters   return (a > b) ? a : b;}
Call method and pass real parameters

 

// Invocation: formal parameters substituted by actual parametersint maximum = max(55, 66);   // 55 and 66 are actual parametersint a = 77, b = 88;maximum = max(a, b);         // a and b are actual parameters
Return to java. util. List If we create a List At this time, the form parameter type E receives a real parameter type. In this way, we actually use this real parameter type Integer when using E.

 

Formal type parameter naming rules

Generally, an uppercase letter is used as the type parameter. For example:

 

Indicates the type of a set element.
Indicates a type.
Key and Value Type
Indicates a number.
S, U, V, and so on indicate the second, third, and fourth type parameters.
Examples of generic classesA GenericBox is created below, which receives a generic type E, indicating a content type. This parameterized type E is used in constructors, getter, and setter, the toString method returns its actual type.

 

 

public class GenericBox
         
           {   // Private variable   private E content;    // Constructor   public GenericBox(E content) {      this.content = content;   }    public E getContent() {      return content;   }    public void setContent(E content) {      this.content = content;   }    public String toString() {      return content + " (" + content.getClass() + ")";   }}
         

The following uses different types (String, Integer and Double) to detect the GenericBoxes class, note that JDK 1.5 also introduces automatic packing and unboxing operations for basic data types and corresponding object types.

 

 

public class TestGenericBox {   public static void main(String[] args) {      GenericBox
         
           box1 = new GenericBox
          
           ("Hello");      String str = box1.getContent();  // no explicit downcasting needed      System.out.println(box1);      GenericBox
           
             box2 = new GenericBox
            
             (123);  // autobox int to Integer      int i = box2.getContent();       // downcast to Integer, auto-unbox to int      System.out.println(box2);      GenericBox
             
               box3 = new GenericBox
              
               (55.66); // autobox double to Double double d = box3.getContent(); // downcast to Double, auto-unbox to double System.out.println(box3); }}
              
             
            
           
          
         

Output result:

 

Hello (class java.lang.String)123 (class java.lang.Integer)55.66 (class java.lang.Double)

Removal type
From the above example, we can see that it seems that the compiler replaces the parameterized type E with the actual type (such as String and Integer) in the class instance. In fact, the compiler replaces all the parameterized types E in the class with the Object type, but the compiler checks and converts the input real type parameters, in this way, it can be compatible with non-generic classes, and the same class can use all object type parameters. This process is called type removal.

Next we will return the example of MyArrayList we wrote above. We know that it is not a generic type. Next we will write a generic version.

 

// A dynamically allocated array with genericspublic class MyGenericArrayList
         
           {   private int size;     // number of elements   private Object[] elements;      public MyGenericArrayList() {  // constructor      elements = new Object[10];  // allocate initial capacity of 10      size = 0;   }      public void add(E e) {      if (size < elements.length) {         elements[size] = e;      } else {         // allocate a larger array and add the element, omitted      }      ++size;   }      public E get(int index) {      if (index >= size)         throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);      return (E)elements[index];   }      public int size() { return size; }}
         

Parser
MyGenericArrayList Declares a generic class with type parameter E. during actual instantiation, for example, MyGenericArrayList , Actual type Will replace parameter type E. The compiler's processing of generics is actually to convert or rewrite the generic code into non-generic code, which ensures backward compatibility. This process is to remove the type, for example, to remove the ArrayList Converting to ArrayList, that is, the type parameter E is replaced by the Object type by default. When the type does not match, the compiler inserts the type conversion operation.

 

MyGenericArrayList The form after being processed by the compiler is as follows:

 

// The translated codepublic class  MyGenericArrayList {   private int size;     // number of elements   private Object[] elements;      public MyGenericArrayList() {  // constructor      elements = new Object[10];  // allocate initial capacity of 10      size = 0;   }      // Compiler replaces E with Object, but check e is of type E, when invoked to ensure type-safety   public void add(Object e) {      if (size < elements.length) {         elements[size] = e;      } else {         // allocate a larger array and add the element, omitted      }      ++size;   }      // Compiler replaces E with Object, and insert downcast operator (E
             
              ) for the return type when invoked   public Object get(int index) {      if (index >= size)         throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);      return (Object)elements[index];   }      public int size() {       return size;    }}
             

When a class is instantiated using a real type parameter, for example, MyGenericArrayList In this way, the compiler will ensure that add (E) can only operate on the String type. When get () is used to return the object of Type e, the compiler also inserts a type conversion operation to match the String type.

For example

 

public class MyGenericArrayListTest {   public static void main(String[] args) {      // type safe to hold a list of Strings      MyGenericArrayList
              
                strLst = new MyGenericArrayList
               
                ();         strLst.add("alpha");   // compiler checks if argument is of type String      strLst.add("beta");         for (int i = 0; i < strLst.size(); ++i) {         String str = strLst.get(i);   // compiler inserts the downcasting operator (String)         System.out.println(str);      }         strLst.add(new Integer(1234));  // compiler detected argument is NOT String, issues compilation error   }}
               
              

As you can see, generics and non-generics are actually the same, but for generics, the compiler checks the types of the imported real types during compilation to ensure the consistency of the types, this avoids the type security issues during running.

Different from the template in C ++, in C ++, a new class is created for each specified parameter type, but in Java, after a generic class is compiled, it can be used by each specified type parameter.

 

2.2 generic Method
The method can also be defined as a generic type, for example:

 

public static 
              
                void ArrayToArrayList(E[] a, ArrayList
               
                 lst) {   for (E e : a) lst.add(e);}
               
              
In a generic method, you need to declare the type parameter before the return type, so that the type parameter can be used in the parameter list of the method or the return type.

 

Similar to generic classes, when the compiler uses the Object type to replace parameter type E, for example, the above Code is processed by the compiler in the following format:

 

public static void ArrayToArrayList(Object[] a, ArrayList lst) {  // compiler checks if a is of type E[],                                                                  //   lst is of type ArrayList
              
                  for (Object e : a) lst.add(e);                                 // compiler checks if e is of type E}
              

Similarly, the compiler adds a type check operation to ensure the type security. It checks whether a is of Type E [] and whether lst is of Type ArrayList. , Whether e is type E, where parameter type E is dynamically determined based on the input real type.
import java.util.*;public class TestGenericMethod {      public static 
               
                 void ArrayToArrayList(E[] a, ArrayList
                
                  lst) {      for (E e : a) lst.add(e);   }      public static void main(String[] args) {      ArrayList
                 
                   lst = new ArrayList
                  
                   ();         Integer[] intArray = {55, 66};  // autobox      ArrayToArrayList(intArray, lst);      for (Integer i : lst) System.out.println(i);         String[] strArray = {"one", "two", "three"};      //ArrayToArrayList(strArray, lst);   // Compilation Error below   }}
                  
                 
                
               

 

In addition, in a generic method, an optional syntax for a generic method is to specify the type in the generic method. You can place the specified real type between the vertex operator and the method name.

TestGenericMethod.
               
                ArrayToArrayList(intArray, lst);
               
This syntax can increase the readability of the Code. In addition, you can specify the generic type where the type is fuzzy.

2.3 wildcard characters

For the following line of code

ArrayList lst = new ArrayList
                
                 ();
                
It may cause type incompatibility errors because of the ArrayList Not an ArrayList . This problem seems to confuse the concept of polymorphism, because in polymorphism, subclass instances can be assigned to the parent class for reference.

Corresponding code:

List
                 
                  
StrLst = new ArrayList
                  
                   
(); // 1 List
                   ObjLst = strList; // 2-Compilation Error unrestricted wildcard
                    
                  
                 
As above, the second line of code will produce a compilation error, but if the second line of code is successful, another problem will occur: Any object can be added to strList, this causes type insecurity.

Corresponding to the above problem, we can see that if you want to write a method printList (List <.>) to print all the elements of the List, if we define the method as printList (ListLst), then the method can only accept ListType parameter. The parameter List is not acceptable. Or List For example:
import java.util.*;public class TestGenericWildcard {      public static void printList(List lst) {  // accept List of Objects only,                                                      // not List of subclasses of object      for (Object o : lst) System.out.println(o);   }      public static void main(String[] args) {      List objLst = new ArrayList();      objLst.add(new Integer(55));      printList(objLst);   // matches         List
                        
                          strLst = new ArrayList
                         
                          ();      strLst.add("one");      printList(strLst);  // compilation error   }}
                         
                        

Unrestricted wildcard characters

To solve this problem, a wildcard (?) is introduced in the wildcard (?), It represents any unknown type. For example, we can override the printList () method above. It can accept any List of unknown types.

public static void printList(List
                      lst) {  for (Object o : lst) System.out.println(o);}

Upper Limit wildcard

The wildcard character indicates the accept type and its subclass, for example:

public static void printList(List
                      lst) {  for (Object o : lst) System.out.println(o);}

List accepts List of Number and Number subtypes, for example: List And List .

Obviously, it can be understood as because it can accept any object type.

Lower limit wildcard

Similar to the upper limit wildcard, it indicates that the accepted types are type and type parent classes.

2.4 restricted Generic

When using generics, we can also use the above restrictions to specify the parameter type. For example: Receives the Number and its subclass (such as Integer and Double)

Example
The following method adds () to declare the parameter type

public class MyMath {   public static 
                       
                         double add(T first, T second) {      return first.doubleValue() + second.doubleValue();   }    public static void main(String[] args) {      System.out.println(add(55, 66));     // int -> Integer      System.out.println(add(5.5f, 6.6f)); // float -> Float      System.out.println(add(5.5, 6.6));   // double -> Double   }}
                       

How does the compiler treat restricted generics?
As we have said above, by default, all generic types will be replaced by the Object type, but the restricted types will be somewhat different. For example, the generic type in will be replaced by the Number type.

For example:

public class TestGenericsMethod {   public static 
                       
                        > T maximum(T x, T y) {      return (x.compareTo(y) > 0) ? x : y;   }      public static void main(String[] args) {      System.out.println(maximum(55, 66));      System.out.println(maximum(6.6, 5.5));      System.out.println(maximum("Monday", "Tuesday"));   }}
                       

By default, Object is the upper limit type of all parameter types, >, It indicates that the maximum type is Comparable. Therefore, the compiler converts the parameter type to Comparable.
public static Comparable maximum(Comparable x, Comparable y) {   // replace T by upper bound type Comparable                                                                 // Compiler checks x, y are of the type Comparable                                                                 // Compiler inserts a type-cast for the return value   return (x.compareTo(y) > 0) ? x : y;}

When a method is called, for example, maximum (55, 66), the basic data type int Is boxed as an Integer object, and then implicitly converted to the Comparable type, the compiler checks the type to ensure the type security. For the returned type, it also explicitly inserts the type conversion operation.
(Comparable)maximum(55, 66);(Comparable)maximum(6.6, 5.5);(Comparable)maximum("Monday", "Tuesday");

We do not need to pass real type parameters to generic methods, because the compiler will automatically determine the parameter type based on the input parameters and convert the types.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.