As described earlier, you can use type parameters when defining classes and interfaces, which can be used as normal types in the method definitions and Field definitions of the class and in the method definitions of the interfaces. In other cases, we do not use type parameters when defining classes, interfaces, but it is also possible to define a method when you want to define a type parameter, and Java 5 also provides support for generic methods.
1, defining a generic method
Suppose you need to implement a method----This method is responsible for adding all elements of an Object array to a Collection collection. Consider using the following code to implement the method.
static void Fromarraytocollection (object A, collection<object> c) {for (object o:a) {C.add (o);}}
The method defined above does not have any problem, the key is the C parameter in the method, its data type is collection<object>. As previously described,,collection<string> is not a subtype of collection<object>-so this method has very limited functionality, and it can only copy elements of an Object array to Object (the subclass of object cannot) Collection the collection, that is, the following code will cause a compilation error.
String Strarr = {"A", "B"}; list<string> strlist = new arraylist<>; Collection<string> object cannot be used as collection<object>, the following code is compiled error fromarraytocollection (Strarr, strList);
It is visible that the parameter type of the above method can not use COLLECTION<STRING> Is it possible to use wildcard collection<?>? Obviously not, we can't put the object into a collection of unknown types.
To solve this problem, you can use the generic method provided by Java 5 (Generic). The so-called generic method is to define one or more type parameters when declaring a method. The usage format of a generic method is as follows:
Modifier <t, s> return value type method name (formal parameter list) {//Method body ...}
Comparing the format of the above method with the format of the common method, it is not difficult to find that the method signature of the generic method is more than the method signature of the ordinary method, the type parameter declaration is enclosed in angle brackets, and the multiple type parameters are separated by commas, and all type parameter declarations are placed between the method modifier and the method return value type.
Using a method that supports generics, you can change the Fromarraytocollection method above to the following form:
static <T> void Fromarraytocollection (t A, collection<t> c) {for (T o:a) {c.add (o);}}
The following procedure demonstrates the complete usage.
Package Com.sym.demo4; Import java.util.ArrayList; Import java.util.Collection; public class Genericmethodtest {//declares a generic method with a T-type parameter, static <T> void Fromarraytocollection (t A, collection& Lt T> c) {for (T o:a) {c.add (O),}} public static void Main (String args) {Object OA = new object[100]; Collection<object> CO = new arraylist<>; In the code below, T stands for Object type fromarraytocollection (OA, CO); String sa = new string[100]; Collection<string> cs = new arraylist<>; The following code in T stands for String type fromarraytocollection (SA, CS); The following code in T represents the Object type fromarraytocollection (SA, CO); Integer ia = new integer[100]; Float FA = new float[100]; Number na = new number[100]; collection<number> cn = New arraylist<>; In the code below, T represents the number type fromarraytocollection (IA, CN); The following code in T represents the number type fromarraytocollection (FA, CN); In the code below, T represents the number type fromarraytocollection (Na, CN); In the code below, T stands for Object type fromarraytocollection (NA, CO); T in the following code represents a String type, but Na isA number array//Because number is neither a String type nor a subclass of it//so there is a compile error//fromarraytocollection (NA, CS); } }
The above program defines a generic method in which a T-type parameter is defined, and the T-type parameter can be used as a normal type within the method. Unlike an interface, a type parameter defined in a class declaration, a parameter defined in a method declaration can only be used in that method, whereas a type parameter defined in an interface, class declaration, is used in the entire interface, class.
Unlike a generic parameter in a class or interface, a generic parameter in a method does not have to pass in the actual type argument explicitly, as shown in the program above, and when the program calls the Fromarraytocollection method, it is not necessary to pass in a String, Object, or other type before calling the method. However, the system can still know the data type of the type parameter, because the compiler infers the value of the type parameter based on the argument, and it usually infers the most direct type parameter. For example, the following code is called:
Fromarraytocollection (SA, CS);
In the above code, CS is a collection<string> type, compared to Fromarraytocollection (T A, collection<t> C) When the method is defined--only the generic parameters are compared. It is not difficult to find that the actual type represented by the T-type parameter is of type String.
For the following calling code:
Fromarraytocollection (IA, CN);
The CN above is the collection<number> type, compared to the method signature of this method-only the generic parameter is compared, it is not difficult to find that the T type parameter represents the number type.
In order for the compiler to accurately infer the type of the type parameter in the generic method, do not create confusion! Once the system is confused, you are wrong! See the following procedure.
Package Com.sym.demo4; Import java.util.ArrayList; Import java.util.Collection; Import java.util.List; public class Errortest {//declares a generic method with a T-type parameter, static <T> void Test (collection<t> from, collection<t > To) {for (T ele:from) {To.add (ele)}, public static void main (String args) {list<object> as = new ArrayList <>; list<string> ao = new arraylist<>; The following code will cause a compilation error test (as, AO); } }
The test method is defined in the above program, which is used to copy the elements from the previous collection into the next collection, the two parameters from the method, the to type is COLLECTION<T>, which requires that the method is called when the two set arguments in the same generic type, Otherwise, the compiler cannot accurately infer the type of the type parameter in the generic method.
The above program calls the test method to pass in two actual parameters, where the data type of as is List<string>, while the data type of AO is list<object>, compared with the generic method signature: Test (Collection <t>a, collection<t> c). The compiler does not correctly identify the actual type that T represents. To avoid this error, you can change the method to the following form:
Package Com.sym.demo4; Import java.util.ArrayList; Import java.util.Collection; Import java.util.List; public class Righttest {//declares a generic method with a T-type parameter, static <T> void Test (collection<? extends t> from, Colle Ction<t> to) {for (T ele:from) {to.add (ele),}} public static void Main (String args) {list<object> ao = new arraylist<>; List<string> as = new arraylist<>; The following code is completely normal test (as, AO); } }
The code above changed the signature of the test method and changed the previous parameter type of the method to Collection<. Extends T> This takes the representation of a type wildcard, as long as the element type in the previous Collection collection of the test method is a subclass of the element type in the latter Collection collection.
So here's the question: when exactly is a generic method used? When do you use a type wildcard character? The differences between generic methods and type wildcards are described in detail below.
2, the difference between generic methods and type wildcard characters
A generic method can be used most of the time instead of a type wildcard character. For example, for Java's Collection interface, two method definitions are:
Public interface collection<e>{Boolean containall (collection<?> c); Boolean AddAll (collection<? extends e> c); ... }
The formal parameters of the two methods in the above collection are either in the form of type wildcard or in the form of a generic method, as shown below.
Public interface collection<e>{Boolean <T> containall (collection<t> c); Boolean <t extends E> ad DAll (collection<t> c); ... }
The above method uses the <t extends e> generic form, when defining the type parameter when setting the upper bound (where E is the type parameter defined in the Collection interface, where e can be used as a normal type).
The type parameter T in the above two methods is used only once, and the only effect that the type parameter T produces is that it can pass in different actual types at different call points. In this case, wildcard characters should be used: wildcards are designed to support flexible subclasses.
A generic method allows a type parameter to be used to represent a type dependency between one or more parameters of a method, or a method returns a type dependency between a value and a parameter. If you do not have such a type dependency, you should not use a generic method.
If the type of one parameter (a) or the type of the return value in a method depends on the type of another parameter (b), the type declaration of the parameter (b) should not use a wildcard----because the type of the formal parameter (a) or return value depends on the type of the parameter (b), if the type of the parameter (b) cannot The program cannot define the type of the formal parameter (a). In this case, you can only consider using declaring a type parameter in a method signature-that is, a generic method.
If necessary, we can use both generic methods and wildcard characters, such as the Java Collections.copy method.
public class collections{public static <T> void copy (list<t> dest, list<? extends t> src) {...}
There is a significant dependency between dest and Src in the copy method above, and the elements copied from the source list must be "dropped" into the target list, so the type of the source list collection element can only be a subtype of the type of the target collection element or itself. However, the JDK defines the SRC parameter type using a type wildcard, not a generic method. This is because the method does not need to add elements to the SRC collection, nor does it need to modify the elements in the SRC collection, so you can use the type wildcard character without using a generic method.
Of course, you can change the method signature above to use a generic method without using the type wildcard character, as shown below.
Class collections{public static <t, S extends t> void copy (list<t> dest, list<s> scr) {...} ...}
This method signature can replace the previous method signature. But note that the above type parameter s it only makes a dump once, no other parameters of the type, the method returns the value of the class plow depends on it, that type parameter s does not exist necessary, that is, you can use wildcards instead of S. Using wildcards is clearer and more accurate than using a generic method (explicitly declaring a type parameter in a method signature), so Java designs the method with a wildcard character instead of a generic method.
There is also a significant difference between a type wildcard and a generic method (explicitly declaring a type parameter in a method signature): A type wildcard can define either the type of the formal parameter in the method signature or the type of the variable, but the type parameter in the generic method must be explicitly declared in the corresponding method.
3,java 7 's "Diamond" syntax and generic constructors
Just as a generic method allows you to declare a type parameter in a method signature, Java also allows a type parameter to be declared in the constructor signature, which results in a generic constructor being used.
Once you have defined a generic constructor, the next time you call the constructor, you can not only "infer" the type of the type parameter based on the type of the data parameter, but the programmer can also explicitly specify the actual type for the type parameter in the constructor. As shown in the following procedure.
Package Com.sym.demo4; Class foo{public <T> Foo (T-t) {System.out.println (t);}} public class Genericconstructor {public static void main (String args) The T parameter in the {///generic constructor is String new foo ("Crazy Java Handout");//the T parameter in the generic constructor is Integer new Foo (200);//explicitly specifies that the T parameter in the generic constructor is string//passed to The argument to the Foo constructor is also a string object, completely correct for new <String> Foo ("Crazy Android Handout");//1////explicitly specifies that the argument to the T parameter in the generic constructor is string//passed to the Foo constructor is also Dou Ble object, the following code error is new <String> Foo (12.3);//2}}
The ① code in the above program not only explicitly specifies that the type of the type parameter T in the generic constructor should be a string, and that the parameter value passed to the constructor by the program is also a string type, so the program is finished to normal. However, in ② code, the program explicitly specifies that the type parameter T in the generic constructor should be of type String, but the parameter value actually passed to the constructor is a Double type, so this line of code will cause an error.
The new "Diamond" syntax for Java 7 is described earlier, which allows the constructor to be invoked when a pair of angle brackets are used to represent generic information. However, if the program explicitly specifies the actual type of the type parameter declared in the generic constructor, you cannot use the "diamond" syntax. As shown in the following procedure.
Package Com.sym.demo4; Class myclass<e>{public <T> MyClass (T-t) {System.out.println (the value of the T parameter is: "+ t);}} public Class Genericdiamond Test {public static void main (string args) the E-parameter in the MyClass class declaration is the Integer type of the T-parameter declared in the String type///generic constructor Myclass<st ring> MC1 = new myclass<> (5); explicitly specifies that the T-parameter declared in the generic constructor is an Integer type myclass<string> MC2 = new <Integer> myclass<string> (5); The e-parameter in the MyClass class declaration is a String type//If you explicitly specify that the T-parameter declared in the generic constructor is an Integer type//You cannot use the "diamond" syntax at this time, the following code is the wrong//myclass<string> MC3 = new <Integer> myclass<> (5); } }
The last line of code in the program above specifies that the type parameter in the generic constructor is an Integer type and you want to use the "diamond" syntax, so this line of code cannot be compiled.
4, set the low limit of the wildcard character
Assume that you implement a tool method: the ability to copy elements from the SRC collection into the Dest collection, because the Dest collection can hold all the elements in the SRC collection, so the type of the Dest collection element should be the parent class of the SRC collection element type. To represent a type dependency between two parameters, consider using wildcards, generic parameters to implement the method at the same time. The code is as follows:
public static <T> void copy (collection<t> dest, collection<. extends t> src) {for (T ele:src) {Dest.ad D (Ele); } }
The above method implements the preceding function. Now assuming that the method requires a return value and returns the last copied element, the above method can be changed to the following form:
public static <T> T-copy (collection<t> dest, collection<? extends t> src) {T last = null; for (t ele:s RC) {last = Ele, Dest.add (ele);} return last; }
On the surface, the above method implements this function, and there is actually a problem: when iterating through the elements of the SRC collection, the type of the SRC element is indeterminate (but certainly it is a subclass of T), and the program can only use T to generically represent the element types of the various SRC sets. For example, the following code:
list<number> LN = new arraylist<>; list<integer> li = new arraylist<>; The following code causes a compilation error for Integer last = copy (ln, LI);
The type of ln in the above code is LIST<NUMBER>, and comparing the formal parameter type signed with the Copy method, the actual type of T is number, not the Integer type-that is, the return value of the Copy method is also a type The Teger type, but actually the element type of the latter copy element must be an Integer. That is, the program loses the type of the SRC collection element during the copy of the collection element.
For the previous copy method, you can understand the dependencies between the two set parameters in this way: regardless of the type of the SRC collection element, as long as the Dest collection element is the same type as the former or the parent class of the former. To express this constraint, Java allows you to set the lower bound of the wildcard character: <? Super Type> This wildcard indicates that it must be the type itself, or the parent class of type. The following procedure overrides the previous copy method by setting the lower limit of the wildcard.
Package Com.sym.demo4; Import java.util.ArrayList; Import java.util.Collection; Import java.util.List; public class Myutils {//dest the type of the collection element must be the same as the type of the SRC collection element, or its parent class public static <T> T copy (collection<? Super T> ; Dest, collection<t> src) {T last = null, for (T ele:src) {last = Ele; Dest.add (ele);} "return last;" Public Stati c void Main (String args) {list<number> ln = new arraylist<>; list<integer> li = new arraylist<>; Li.add (5); Here it is accurate to know that the last element being copied is an integer type//Same as the type of the SRC collection element, integer last = copy (ln, li);//1 System.out.println (LN); } }
Using this statement, you can ensure that the ① of the program after the invocation of a copied element type is an Integer, rather than a general number type.
In fact, treeset<e> from a constructor in the Java collection framework also uses this syntax to set the lower bound of a wildcard, as shown below.
The following E is the type parameter TreeSet (COMPARATOR<? super e> C) when defining the TreeSet class;
As described in the previous chapter, TreeSet sorts the elements in the collection in either a natural order or a custom order. If you need TreeSet to customize the ordering of all the meta-cables in the collection, the TreeSet object is required to have a Comparator object associated with it. The parameter C in the above constructor is the Comparator object for the custom ordering.
Comparator-U is also an interface with a generic declaration:
public interface comparator<t>{int compare (t FST, T snd);}
With this low-bound wildcard syntax, you can flexibly select the appropriate Comparator when creating a TreeSet object. Suppose you need to create a treeset<string> set, and a Comparator that can be compared to a String size, which Comparator can be either comparator<string> Comparator<object>----As long as the type of the descendant in the angle brackets is the parent type of String (or itself). As shown in the following routines.
Package Com.sym.demo4; Import Java.util.Comparator; Import Java.util.TreeSet; The actual type of public class Treesettest {public static void main (String args) {//Comparator is the parent class of the actual type in TreeSet, satisfying the requirements treeset<st ring> ts1 = new Treeset<> (new comparator<object> {public int compare (object FST, Object snd) {return hash Code > Snd.hashcode? 1:hashcode < Snd.hashcode? -1:0; } } ); Ts1.add ("Hello"); Ts1.add ("WA"); treeset<string> ts2 = new Treeset<> (new comparator<string> {@Override public int compare (String First, String second) {return first.length > second.length? -1:first.length < second.length? 1:0;}} ); Ts2.add ("Hello"); Ts2.add ("WA"); System.out.println (TS1); System.out.println (TS2); } }
By defining the parameters of the TreeSet constructor by using the lower bound of this wildcard, all available Comparator are passed as parameters, which increases the flexibility of the program. Of course, not only do TreeSet have this usage, TREEMAP also have similar usage, see the Java API documentation for more details.
5, generic method and method overloading
Because generics allow the upper bound of wildcards to be set, the lower bounds of wildcards are allowed, allowing the inclusion of the following two method definitions in a class.
public class myutils{public static <T> void copy (collection<t> dest, collection<? extends t> src) {.. .} 1 public static <T> copy (COLLECTION<? super t> dest, collection<t> src) {...} 2}
The above Myutils class contains two copy methods, the parameter lists of the two methods are somewhat different, but the difference is not very clear: the two parameters of the two methods are Collection objects, the collection element type in the previous collection is the parent class of the collection element type in the latter collection. If this class contains only these two methods there will be no errors, but the call to this method will cause a compilation error. For example, for the following code:
list<number> LN = new arraylist<>; list<integer> li = new arraylist<>; Copy (ln, LI);
The copy method is called in the bold part of the program above, but the copy method can match the ① copy method, at which point the type of the T type parameter is number: You can also match the ② copy method, at which time the type of the T parameter is integer. The compiler cannot determine which copy method this line of code wants to invoke, so this line of code will cause a compilation error.
Generic method of Java learning path