Abstract
This article is based on chapter 8 of Java Core Technology Volume 1. Some of the articles are excerpted from the book as personal notes. The article will not be too deep, hope the reader reference is good.
Why use generic programming
Generic programming means that you write code that can be reused by many different types of objects.
Benefits of type parameters
Before the advent of generic classes, the ArrayList class maintained only an array of Object references:
public class ArrayList {
private Object[] elementData; // It is used to store data
public Object get(int i) {....}public void add(Object o) {....}... }Copy the code
The problem
1. A cast must be performed when obtaining a value. 2. You can add objects of any class to the arraylist, and if the array is not of the same type, it will be an error to force the result of get to be typed.
Generics provide a better solution: type parameters:
ArrayList<String> array = new ArrayList<String>():
Copy the code
Using the type parameter information, we can keep the type uniform when we add data, and we don’t need to cast when we call get because we define the type at initialization and the compiler recognizes the type of the returned value and converts it for us.
Define a simple generic class
public class Pair<T> {
private T num;
public Pair(T num) { this.num = num; }
public T getNum(a) { return num; }
public void setNum(T num) { this.num = num; }}Copy the code
We can see that after the Pair class name, we have added a type variable of the generic class, and there can be multiple type variables, such as
public class Pair<T.U> {... }Copy the code
If we instantiate the Pair class, for example:
new Pair<String>;
Copy the code
We can then imagine the Pair class as follows:
public class Pair<String> {
private String num;
public Pair(String num) { this.num = num; }
public String getNum(a) { return num; }
public void setNum(String num) { this.num = num; }}Copy the code
Is it easy? In the Java library, the variable E represents the element type of the collection, K and V represent the key and value types of the table respectively, and T, U, and S represent any type.
Generic method
Define a method with type parameters
public static <T> T getMiddle(T... a) {
return a[a.length / 2];
}
Copy the code
You can see that the type variable (< T >) is placed after the modifier (public static) and before the return type (T). Generic methods can be defined in ordinary or generic classes.
Qualification of a type variable
If we need to constrain a type variable, for example, the incoming variable must implement the Comparable interface because it needs to call compareTo’s methods. This allows us to qualify variables using the extends keyword.
public <T extends Comparable> T max(T a) { a.compareTo(...) ; . }Copy the code
Whether a variable needs to be qualified to inherit from a class or implement an interface, it is qualified using the extends keyword.
Generic code and virtual machines
Type erasure
No matter how we define a generic class or method in our code, we provide a corresponding primitive type. The name of the primitive type is the name of the generic class with the type parameters removed. For example, the primitive type of
is Object and the primitive type of
is MyClass. The code looks like this:
public class Pair<T> {
private T property;
public Pair(T property) {
this.property = property; }}Copy the code
After type erasure:
public class Pair<Object> {
private Object property;
public Pair(Object property) {
this.property = property; }}Copy the code
Translate generic expressions
If the return type is erased, the compiler inserts a cast as follows:
Pair<Employee> buddies =.. Employee buddy = buddies.getfirst ();Copy the code
Erasing the return type of getFirst will return Object, but the compiler will automatically help us force the conversion to Employee. So: the compiler divides this method into two instructions:
The call to the original method Pair. GetFirst casts the returned Object type to Employee
Section Summary:
There are no generics in the virtual machine, only ordinary classes and methods. All type parameters are replaced by their qualified types to preserve type safety. If necessary, cast bridge methods are synthesized to preserve polymorphism.
Constraints and Limitations
Type parameters cannot be instantiated with primitive types
You can’t instantiate type parameters with eight basic data types, and you’ve never seen code like ArrayList
. Only the ArrayList < Integer >. The reason is that the basic data types are not Object. So it can only be replaced with their package type.
Runtime type queries apply only to primitive types
All type queries only produce primitive types, because there is no such thing as generic types in the virtual machine. Such as:
Pair<String> pair = new Pair<String>("johnson");
if (pair instanceof Pair<String>) {} // Error
if (pair instanceof Pair<T>) {} // Error
if (pair instanceof Pair) {} // Pass
Copy the code
Cannot create an array of parameterized types
Pair<String>[] pairs = new Pair<String>[10]; //error
Copy the code
Why can’t we define it this way? Since pairs are of type Pair[] and can be converted to Object[], ArrayStoreException is thrown if an attempt is made to store an element of another type.
pairs[0] = 10L; / / thrown exception
Copy the code
In a word, not rigorous. So you can’t create arrays of parameterized types.
Type variables cannot be instantiated
Cannot use new T(…) , new T […]. Or tc lass. Because when the type is erased, T will become Object, and we certainly don’t want to instantiate Object. After Java8, however, we can implement it using the Supplier
interface, which is a functional interface that represents a function with no arguments and a return type of T:
public class Pair<T> {
private T first;
private T second;
private Pair(T first, T second) {
this.first = first;
this.second = second;
}
public static <T> Pair<T> makePair(Supplier<T> constr) {
return new Pair<>(constr.get(), constr.get());
}
public static void main(String[] args) {
Pair<String> pair = Pair.makePair(String::new); }}Copy the code
A static context type variable in a generic class is invalid
You cannot reference a type variable in a static field or method. Such as:
public class Pair<T> {
private static T instance; //Error
public static T getInstance(a) { //Error
if (instance == null)
instance = new Pair<T>();
returninstance; }}Copy the code
Because a type variable (
) in a class is scoped in an object, not in a class. If you want to use generic methods, you can refer to generic methods in this article
An instance of a generic class cannot be thrown or caught
Objects that cannot throw or catch generic classes, or even extend Throwable, are illegal:
public class Pair<String> extend Exceotion {} //Error
Copy the code
public static <T extends Throwable> void doWork(Class<T> t) {
try{... }catch (T e) { //Error. }}Copy the code
However, it is possible to use type variables for an exception after it has been thrown (I don’t think I’ve ever seen such code).
public static <T extends Throwable> void doWork(Class<T> t) throw T { //Pass
try{... }catch (Exception e) {
throwe; }}Copy the code
Inheritance rules for generic types
For example, the Manager class inherits from the Employee class. But Pair
and Pair
are not related. As in the following code, an error is reported and the delivery failed:
Pair<Manager> managerPair = new Pair<Manager>();
Pair<Employee> employeePair = managerPair; //Error
Copy the code
Wildcard type
Wildcard concept
Wildcard types allow type parameters to change, using? Identify wildcard types:
Pair<? extends Employee>
Copy the code
If the Pair class is as follows
public class Pair<T> {
private T object;
public void setObject(T object) {
this.object = object; }}Copy the code
Using wildcards solves the problem of inheritance rules for generic types, such as:
Pair<Manager> managerPair = new Pair<Manager>();
Pair<? extends Employee> employeePair = managerPair; //Pass
Manager manager = new Manager();
employeePair.setObject(manager); //Error
Copy the code
Use the
, the compiler only knows that employeePair is a subclass of Employee. It does not know what subclass employeePair is, so the last step employeepair.setobject (Manager) cannot be executed.
Supertype qualification for wildcards
Wildcards have the added ability to specify a supertype qualification, such as:
public class Pair<T> [
...
public static void salary(Pair<? super Manager> result) {
//...
}
}
Copy the code
This wildcard is for all supertypes of Manager (including Manger), for example:
Pair<Manager> managerPair = new Pair<Manager>();
Pair<Employee> employeePair = new Pair<Employee>();
Pair.salary(managerPair); //Pass
Pair.salary(employeePair); //Pass
// If ManagerChild is a Manager subclass
Pair<ManagerChild> managerChildPair = new Pair<ManagerChild>();
Pair.salary(managerChildPair); //Error
Copy the code
Unqualified wildcard
Unqualified wildcards, such as Pair
public static boolean hasNull(Pair
pair) {
return pair.getObject() == null;
}
Copy the code
To tell you the truth, the wildcard made my head dizzy, repeatedly read the article, began to understand slowly, I was too difficult… This is the end of the article, I do not know if you understand, did not understand it may be my skills and article writing ability is still to be improved, friends can also go to see the Java core technology volume 1, this book, feel very good. Recently, I picked up this book and found that the foundation is very important. If the foundation is settled well, it will be easier to start with other technical points and know what it is. Recently, I like this sentence very much: “Great oaks from little acorns grow, do not build a platform on floating sand”.
Personal blog: colablog.cn/
If my article helps you, you can follow my wechat official number and share the article with you as soon as possible