Home >php教程 >PHP开发 >Generics - Introduction to Generics

Generics - Introduction to Generics

高洛峰
高洛峰Original
2016-12-19 16:02:195190browse

Introduction to Generics

Let’s take an example to illustrate what generics are.
There are two classes as follows. It is necessary to construct objects of the two classes and print out their respective members x.
public class StringFoo {
  private String x;
  public String getX() {
      return x;
  }

  public void setX(String x) {
      this.

public class DoubleFoo {
private Double x;
public Double getX() {
return x;
}

public void setX(Double x) {
this.x = x;
}
}
If you want to implement the ong, Date, etc. It is really boring to write corresponding classes for type operations.
Therefore, refactor the above two classes into one class, consider the following:
In the above class, the logic of the members and methods are the same, but the types are different. Object is the parent class of all classes, so you can consider using Object as the member type, so that it can be universal.
public class ObjectFoo {
  private Object x;
  public Object getX() {
      return x;
  }

  public void setX(Object x) {
    this.

The calling code is as follows :
public class ObjectFooDemo {
public static void main(String args[]) {
ObjectFoo strFoo = new ObjectFoo();
strFoo.setX("Hello Generics!");
ObjectFoo douFoo = new ObjectFoo() ;
                                                                                                                                                                                                                                                                                                   .setX(new Double("33"));
ObjectFoo objFoo = new ObjectFoo();
objFoo.setX(new Object());

String str = (String)strFoo.getX();
Double d = (Double)douFoo.getX();
Object obj = objFoo.getX();

System.out.println("strFoo.getX=" + str);
System.out.println("douFoo.getX=" + d);
System.out.println("strFoo.getX=" + obj);
}
}
The above is the code we wrote without generics, using the top-level base class Object for type declaration , and then pass the value in, and perform forced type conversion when taking it out.
JDK has introduced the concept of generics since 1.5 to elegantly solve such problems. Using generic technology, the code written is as follows:
public class GenericsFoo {
  private T x;
  public T getX() {
    return x;
  }

  public void setX(T x) {
     this. x = x;
}
}

The calling code is as follows:
public class GenericsFooDemo {
public static void main(String args[]){
GenericsFoo strFoo=new GenericsFoo();
           strFoo. setX("Hello Generics!");
        GenericsFoo douFoo=new GenericsFoo(); new GenericsFoo( ;Object obj = objfoo.getx (); intln ("strFoo.getX=" + obj);
}
}

Note that there are several obvious changes:
1. When the object is created, the type must be given explicitly, such as GenericsFoo.
2. When the object is retrieved through the getX method, no type conversion is required.
3. When calling each method, if the parameter type does not match the type specified when creating the method, the compiler will report an error.
So why do we need generics? There are two benefits:
1. You can check whether the stored data is correct at compile time. There is a tendency in our development to find errors as early as possible, preferably during the compilation stage. Generics just meet this condition.
2. Reduced forced conversion, String str = (String)strList.get(0); this operation is a downward transformation, which is a relatively dangerous operation. When the object stored in the List is not suitable for String, it will be thrown abnormal.
In JDK1.5, various data type tool classes in the java.util package support generics. They are widely used in programming and need to be mastered.
The most common applications of generics are in classes, interfaces and methods, which are introduced below.
3.4.2 Generics are applied to interfaces:
public interface ValuePair {
public A getA();
public B getB();
public String toString();
}
Here both A and B Is the representative type. In cusp <>, you can use one type or multiple types.
3.4.3 Generics are applied to classes:
public class ValuePairImpl {
public final A first;
public final B second;
public ValuePairImpl(A a, B b) { first = a; second = b; }
      public A getA() { return first; }
      public B getB() { return second; }
  public String toString() {
    return "(" + first + ", "  + second + ")";
}
}
If this class implements a generic interface, the corresponding writing method is:
public class ValuePairImpl implements ValuePair {
...
}
3.4.4 Generics are applied in methods Above:
Generics can also be applied to individual methods, examples are as follows:
public class GenericMethod {
      public void printValue(T v) {
                String str = v.getClass().getName() + “ = " + v.toString();
                System.out.println(str);
}
}
Note the syntax: after the public modifier is <>, then the function return value, then the function name, function parameter. Of course, the return value can also be a generic type.
3.4.5 Limiting the available types of generics
The three generic applications introduced above are a common approach when applied to interfaces, classes, and methods. There are no restrictions on the types that can be passed in to generics. But in some scenarios, we want to limit the available types. For example, we hope that the incoming type must inherit from a certain class (that is, it must be a subclass, grandchild, etc. of a certain class). In this case, Generic restriction syntax is used.
extends: Restrict generic types to be descendants of a certain class, including this type.
Syntax:
Here, T is a generic type, and the extends keyword restricts the generic type to be a descendant of parentClass. parentClass specifies the type of parent class, which can also be an interface.
In the Java language, classes can only be inherited singly, and interfaces can be inherited multiple times. If you want to restrict a specified type to inherit from a certain class and implement multiple interfaces, the syntax is:

Note that the class must be in front of the interface.
An example is as follows:
public class BaseClass {
       int value; 

       public BaseClass(int value) { 
              this.value = value; 
       } 
       
       public int getValue() { 
              return value; 
       } 

       public void setValue(int value) { 
              this.value = value; 
       } 
       


public class SubClass extends BaseClass{ 
       public SubClass(int value) { 
              super(value*2); 
       } 


public class GenericBound
       
       public long sum(List tList) { 
              long iValue = 0; 
              for (BaseClass base : tList) { 
                     iValue += base.getValue(); 
              } 
              
              return iValue; 
       } 

       public static void main(String[] args) { 
              GenericBound obj = new 
GenericBound(); 
              
              List list = new LinkedList(); 
              list.add(new SubClass(5)); 
              list.add(new SubClass(6)); 
              
              System.out.println(obj.sum(list)); 
       } 

运行,输出结果为22. 
接着,我们再深入探讨一下。把上面的例子该写如下: 
public class GenericBound
       
       public long sum(List tList) { 
              long iValue = 0; 
              for (BaseClass base : tList) { 
                     iValue += base.getValue(); 
              } 
              
              return iValue; 
       } 

       public static void main(String[] args) { 
              // 注意!!! 
              // 将obj 的类型由GenericBound改为GenericBound,无法通过编译 
              GenericBound obj = new 
GenericBound(); 
              
List list = new LinkedList(); System.out.println(obj.sum( list));
     }
}
The statement GenericBound obj = new GenericBound(); cannot pass compilation. The fundamental reason is that the at the GenericBound class declaration limits the construction of this class When instantiating, T is a certain type, and this type is a descendant of BaseClass. However, there are many descendants of BaseClass, such as SubClass3 and SubClass4. If you have to write a specific subclass type for each one, it would be too troublesome. It is better to use Object to generalize. Can we use the type of the parent class to introduce instances of various subclasses like ordinary classes? Wouldn't this be much simpler? The answer is yes. Generics provide a better solution for this situation, which is "wildcard generics", which will be explained in detail below.
3.4.6 Wildcard Generics
Java’s generic types are ordinary Java types like java.lang.String and java.io.File. For example, the types of the following two variables are different from each other:
Box boxObj = new Box();
Box boxStr = new Box();
Although String is Object subclass, but there is no relationship between Box and Box - Box is not a subclass or subtype of Box, therefore, the following assignment statement is illegal:
boxObj = boxStr; // Unable to compile
Therefore, we hope that when using generics, we can use the type of the parent class to introduce instances of various subclasses like ordinary classes, thereby simplifying program development. Java's generics provide the ? wildcard character to meet this requirement.
Code example is as follows:
public class WildcardGeneric {
                                                                          use using using using                 through   through use through out through out through out out through off ‐ ‐ ‐ ‐ ‐ ‐ to ( lst.get(i));
                                       ​                                                                                             
              strList.add("One");                                                                        strList.                                                                      strList. intList = new LinkedList();
        intList.add(25 );But in this case, the types that the WildcardGeneric.print method's parameters can accept may be a bit too broad for the programmer's design intent. Because we may just want print to accept a List, but the elements in this List must be descendants of Number. Therefore, we need to impose restrictions on wildcards. In this case, we can use the bounded wildcard form to meet this requirement. Let’s modify the print method:
public void print(List lst) {
                                                                                                                                                                                      through   through   through   out through  through through  through through out   out through out through through out through off ‐ ‐ for (int i = 0-w i < lst.size(); i++) to
public void print(List .get (i));
}}}
This way, list & lt; integer & gt;, list & lt; short & gt; and other types of variables can be passed to the Print method, and the genetic variable type variables of List (such as list & lt (such as list & lt, ;String>) will be illegal to pass to the print method.
Except ? In addition to extends the upper bounded wildcard, we can also use the lower bounded wildcard, such as List.
Finally, let’s summarize the three forms of generic types using wildcards:
GenericType
GenericTypeLet’s take a look at a piece of code first:
public class GenericsFoo {
  private T x;
  public T getX() {
    return x;
  }

  public void setX(T x) {
    this.x = x;
}

public static void main(String[] args) {
                                            out ‐ ‐ ‐           ‐   ‐ ‐                                         –                                                                                            GenericsFoo gf2 = gf;
             gf2.setX("World"); ! !
          String str = gf2.getX(); ! !
                                                                         gf2.setX(gf2.getX()); ! !
}
}
​ Note that the last three lines in the main method are illegal and cannot be compiled. It is originally a generic type of . After being referenced through , setX() reports an error when passing in a String, and the type of the return value of getX() is not String. What's even more strange is that the statement gf2.setX(gf2.getX()); even if you take out the value and then set it back unchanged, it won't work. How is this going?
In order to completely understand these problems, we need to understand the internal implementation principles of generics in JDK. Let’s look at two examples first:
public class GenericClassTest {
                                                              using using using using                   off ’s ’ s  ‐       ‐   ‐ ‐ a () .getClass();
          System.out.println(c1 == c2); c3);
}
}
After running, the output result is:
true
true
This example shows that the generic ArrayList, ArrayList and the ArrayList without generics are actually the same class. It's like not using generics.
                                                                                                                                                                                                 Looking at the second example:
class Element {}          
class Box {            
class Pair  < List list = new ArrayList();                         Map      Pair< ;Integer, String> p = new Pair();
           System.out.println(Arrays .toString(
                      map.getClass()                                                            . s()));
           System.out.println(Arrays.toString (
                                                                                                                                                                                              
Check the JDK Documentation, the Class.getTypeParameters() method returns an array of TypeVariable objects. Each TypeVariable object in the array describes the type declared in the generic. This seems to mean that we can find out the real type when a generic is instantiated from the TypeVariable object. However, from the output of running the program, we can see that the series of TypeVariable objects returned by Class.getTypeParameters() only represent the parameterized type placeholders during generic declaration, and the types during actual instantiation are discarded. . Therefore, the truth about Java generics is:
In generic code, there is no information about parameterized types at all.
The reason for this fact is that JDK uses erasure for the internal implementation of generics. The specific erasure method is as follows:
1) ArrayList, ArrayList, ArrayList are all erased into ArrayList
2) ArrayList, ArrayList are all erased into ArrayList
3) ArrayList
After understanding the implementation mechanism of erasure, let’s go back and analyze the previous example to see why it cannot be compiled:
public class GenericsFoo {
  private T x;
  public T getX() {
           return x; gf = new GenericsFoo ();            gf2.setX("World"); ! !
            String str = gf2.getX(); // Error! ! !
                                                                         gf2.setX(gf2.getX()); ! !
}
}
Due to the erasure mechanism, GenericsFoo are all erased into GenericsFoo. After the type is lost, the corresponding method declaration becomes:
public Object getX();
public void setX(null x);
Therefore, the return value of any get method of a generic class that takes out a value becomes Object and can be called, but the return value needs to be type converted; any set method of a generic class that sets a value, the parameter type All become null, and no type can be converted to null type, so all set methods cannot be called.
This forms an interesting phenomenon: for the generic type of , it can only be get, not set.
The above explains the erasure mechanism of Java generics, resulting in the loss of some useful type information. But we can use some tricks to let the compiler reconstruct the type information so that the set method can be called normally. See the following code:
          public void setGeneric(GenericsFoo foo) {                                         . ; foo) {
          foo.setX(foo.getX ());
}
setGenericHelper() is a generic method. Generic methods introduce additional type parameters (in angle brackets before the return type). These parameters are used to represent the parameters and/or return values ​​of the method. Type constraints between. setGenericHelper () This declaration method allows the compiler (through the type interface) to name the type parameters of the GenericsFoo generic. But a type can have a parent class, a grandparent class, and can also implement multiple interfaces. So which type will the compiler convert to?
We use the following code to verify it:
public class GenericClassTest3 {
        public static String getType(T arg)                                                           using               through ’ s ’ through ’ s through through use using through ’s ’ through ’ s way through ’’s ’ back through ‐ to ‐ ‐ ‐‐ ww‐ ‐ to
                                                         Public static void main( String [] args) {
integer I = New Integer (5);
System.out.println (GenericClastest3.Gettype (i));
}}} After the program runs, the output result is:
java.lang.integer
So the compiler is able to infer that T is an Integer, Number, Serializable, or Object, but it chooses Integer as the most specific type that satisfies the constraints.
In addition, due to the erasure mechanism of generics, we cannot directly use the new operator on generic types, such as:
public class GenericNew ;             // Failed to compile! ! !
                             return obj;
However, in some cases, we still need dynamic instantiation of generic types. For creating a single object and creating an array, the code example is as follows:
public class GenericNew cls.newInstance();
                                                                                         )obj;
} catch(Exception e) {
                                                                                                                                                     . try cls, len);​​​​ }​
}​
In the above code, the create method dynamically creates an instance of a generic type. The createArray method dynamically creates an array of instances of a generic type.




For more articles related to generics - introduction to 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