Java Generics

Java generics is a new feature introduced in JDK 5. Generics provide a compile-time type safety checking mechanism that allows programmers to detect illegal types at compile time.

The essence of generics is parameterized types, that is, the data type being operated on is specified as a parameter.

Suppose we have a requirement like this: write a sorting method that can sort integer arrays, string arrays, or even arrays of any other type. How can this be implemented?

The answer is to useJava generics。

Using the concept of Java generics, we can write a generic method to sort an object array. Then, call that generic method to sort integer arrays, floating-point arrays, string arrays, etc.


Generic Methods

You can write a generic method that can receive parameters of different types when called. Based on the types of arguments passed to the generic method, the compiler handles each method call appropriately.

The following are the rules for defining generic methods:

  • All generic method declarations have a type parameter declaration section (delimited by angle brackets). This type parameter declaration section appears before the method's return type (in the example below, the<E>)。
  • Each type parameter declaration section contains one or more type parameters, separated by commas. A generic parameter, also known as a type variable, is an identifier used to specify a generic type name.
  • Type parameters can be used to declare return types and can act as placeholders for the actual parameter types obtained by the generic method.
  • The declaration of a generic method body is the same as for other methods. Note that type parameters can only represent reference types, not primitive types (such asint、double、charetc.).

Generic markers in Java:

  • E- Element (used in collections, because collections store elements)
  • T- Type (Java class)
  • K- Key
  • V- Value
  • N- Number (numeric type)
  • ?- represents an indeterminate Java type

Example

The following example demonstrates how to use a generic method to print array elements of different types:

Example

public class GenericMethodTest { //Generic method printArray public static < E > void printArray( E[] inputArray ) { //Printing array elements for ( E element : inputArray ){ System.out.printf( "%s ", element ); } System.out.println(); } public static void main( String args[] ) { //Creating arrays of different types: Integer, Double, and Character Integer[] intArray = { 1, 2, 3, 4, 5 }; Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 }; Character[] charArray = { 'H', 'E', 'L', 'L', 'O' }; System.out.println( "Integer array elements are:" ); printArray( intArray ); //Passing an integer array System.out.println( "\nDouble-precision array elements are:" ); printArray( doubleArray ); //Passing a double-precision array System.out.println( "\nCharacter array elements are:" ); printArray( charArray ); //Passing a character array } }

Compile the above code, and the running result is as follows:

整型数组元素为:
1 2 3 4 5 

双精度型数组元素为:
1.1 2.2 3.3 4.4 

字符型数组元素为:
H E L L O 

Bounded Type Parameters:

There may be times when you want to restrict the range of types that are allowed to be passed to a type parameter. For example, a method that operates on numbers might only want to accept instances of Number or Number subclasses. This is the purpose of bounded type parameters.

To declare a bounded type parameter, first list the name of the type parameter, followed by the extends keyword, and then its upper bound.

Example

The following example demonstrates how "extends" is used in the general sense of "extends" (classes) or "implements" (interfaces). The generic method in this example returns the maximum of three comparable objects.

Example

public class MaximumTest { //Compare three values and return the maximum public static <T extends Comparable<T>> T maximum(T x, T y, T z) { T max = x; //Assume x is the initial maximum if ( y.compareTo( max ) > 0 ){ max = y; //y is larger } if ( z.compareTo( max ) > 0 ){ max = z; //Now z is larger } return max; //Return the maximum object } public static void main( String args[] ) { System.out.printf( "The largest of %d, %d, and %d is %d\n\n", 3, 4, 5, maximum( 3, 4, 5 ) ); System.out.printf( "The largest of %.1f, %.1f, and %.1f is %.1f\n\n", 6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) ); System.out.printf( "The largest of %s, %s, and %s is %s\n","pear", "apple", "orange", maximum( "pear", "apple", "orange" ) ); } }

Compile the above code, and the running result is as follows:

3, 4 和 5 中最大的数为 5

6.6, 8.8 和 7.7 中最大的数为 8.8

pear, apple 和 orange 中最大的数为 pear

Generic Classes

The declaration of a generic class is similar to that of a non-generic class, except that a type parameter declaration section is added after the class name.

Like generic methods, the type parameter declaration section of a generic class also contains one or more type parameters, separated by commas. A generic parameter, also known as a type variable, is an identifier used to specify a generic type name. Because they accept one or more parameters, these classes are called parameterized classes or parameterized types.

Example

The following example demonstrates how we define a generic class:

Example

public class Box<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public static void main(String[] args) { Box<Integer> integerBox = new Box<Integer>(); Box<String> stringBox = new Box<String>(); integerBox.add(new Integer(10)); stringBox.add(new String("Example")); System.out.printf("Integer value: %d\n\n", integerBox.get()); System.out.printf("String: %s\n", stringBox.get()); } }

Compile the above code, and the running result is as follows:

整型值为 :10

字符串为 :Example

Type Wildcards

1. Type wildcards generally use?to replace specific type parameters. For exampleList<?>is logicallyList<String>,List<Integer>and allList<specific type arguments>parent classes.

Example

import java.util.*; public class GenericTest { public static void main(String[] args) { List<String> name = new ArrayList<String>(); List<Integer> age = new ArrayList<Integer>(); List<Number> number = new ArrayList<Number>(); name.add("icon"); age.add(18); number.add(314); getData(name); getData(age); getData(number); } public static void getData(List<?> data) { System.out.println("data :" + data.get(0)); } }

The output result is:

data :icon
data :18
data :314

Analysis:BecausegetData()the method parameter isList<?>type, thereforename,age,numberall can be used as arguments to this method; this is the function of wildcards.

2. The type wildcard upper bound is defined through the form Listto define it. Defined in this way, the wildcard generic value accepts Number and its subclass types below it.

Example

import java.util.*; public class GenericTest { public static void main(String[] args) { List<String> name = new ArrayList<String>(); List<Integer> age = new ArrayList<Integer>(); List<Number> number = new ArrayList<Number>(); name.add("icon"); age.add(18); number.add(314); //getUperNumber(name);//1 getUperNumber(age);//2 getUperNumber(number);//3 } public static void getData(List<?> data) { System.out.println("data :" + data.get(0)); } public static void getUperNumber(List<? extends Number> data) { System.out.println("data :" + data.get(0)); } }

Output result:

data :18
data :314

Analysis:In//1An error will occur at the position becausegetUperNumber()the parameter in the method has already limited the generic upper bound of the parameter toNumber, so the generic type beingStringis not within this range, so an error is reported.

3. The type wildcard lower bound is defined through the formList<? super Number>to define it, meaning the type can only acceptNumberand its superclass types above it, such asObjectinstances of the type.

Other Extensions