summary
A collection can store objects of any type. After an Object is stored in a collection, it is promoted to Object type. When I take an Object out of a collection, I need to perform mandatory transformation to operate on the Object.
public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("a11"); list.add(1); list.add("1"); for (Object o : list) { String str = (String) o; System.out.println(str); } }
This simple demo code will show the problem java.lang.ClassCastException Runtime exception for. In this case, you can use instanceof to determine whether it is a type or its subtype, but this will increase the code complexity, and can not completely solve the problem. After all, how to consider the missing types can not complete the normal conversion.
After JDK 1.5, the * * Generic * * syntax was introduced. In this way, when we use the design API, we can specify the class or method or even the generics of the interface. The designed API is also more concise, and we can also find exceptions during compilation.
Benefits of using generics
- Avoid cast issues
- Advance runtime exceptions to compile time, reducing the workload of later bug modification
- Once generics are specified, the data types are unified
- Realize the modularization of code and regard data type as parameter
Definition and use of generics
1. Definition of generic class
(1) Format
public class class name < generic variable >{ ... }
Once the generics in a generic class are specified, the whole class, except for the generic methods, will replace all the places with the type placeholders of the generics with the specified types.
(2) Use case
public class MyClass<T> { private T t; public MyClass() { } public MyClass(T t) { this.t = t; } public T getT() { return t; } public void setT(T t) { this.t = t; } }
public static void main(String[] args) { // Creating objects with empty parameters MyClass<String> mc1 = new MyClass<>(); mc1.setT("Hello"); String t = mc1.getT(); System.out.println(t); System.out.println("-----------------------------------"); // Full parameter construction. Angle bracket specifies the type of data to be passed MyClass<Integer> mc2 = new MyClass<>(1); String t = mc1.getT(); System.out.println(t); }
2. Definition of generic methods
In the use of generic classes, the generic placeholders are usually replaced by the uniformly specified types by default, but sometimes methods are needed to define their own specific generics.
(1) Format
Modifier < T > return value type method name (T t, parameter list...){ Method body }
matters needing attention:
- Before < T > define generics of methods
- Later method parameters: T t t uses its own generics
(2) Use case
public class MyClass02<T> { // Not a generic method public void print(T t){ System.out.println(t); } // Generic method, which defines its own generics and uses placeholder E to represent generics public <E> void show(E e){ System.out.println(e); } }
public static void main(String[] args) { MyClass02<String> mc = new MyClass02<>(); mc.print("Hello"); // print uses generics on the class. It has been determined to be of String type //Error: mc.print(10000); mc.show(18); }
3. Definition of generic interface
(1) Format
public interface interface name < T >{ ... }
(2) Generics defined on the interface, when to determine the specific type
- When defining the implementation class, directly determine the specific types of generics on the interface
public interface MyInter<T> { public abstract void show(T t); }
/* When defining the implementation class, directly determine the specific types of generics on the interface */ public class MyInterImpl implements MyInter<String> { @Override public void show(String s) { System.out.println(s); } }
- When defining an implementation class, it is uncertain about the specific type of the generic on the interface. Then the implementation class must be defined as a generic class, and the generic variables on the implementation class must be consistent with the generic variable names on the interface. When creating an implementation class object, what type is written in < > and what type is the generic. This is also called generics passing.
public interface MyInter<T> { public abstract void show(T t); }
/* Uncertain interface type when defining implementation class */ public class MyInterImplB<T> implements MyInter<T> { @Override public void show(T t) { System.out.println(t); } }
Wildcards for generics
Represents any reference type, which can only be used to match generics, not to define generics.
(1) Precautions:
- There is no polymorphism in generics. When creating a collection object, the content of < > on the left and right sides should be consistent
- What can ArrayList <? > List accept?
Can accept any type of object of ArrayList (as long as you write a reference type in the creation of ArrayList collection object, it is OK)
(2) Example
public static void main(String[] args) { ArrayList<String> l1 = new ArrayList<>(); Collections.addAll(l1,"123-123qwe-qwe-qwe-fhj-ghj-yjw-wlt-gfd-iop".split("-")); ArrayList<Integer> l2 = new ArrayList<>(); Collections.addAll(l2,4,5,6,13,123,123,123,45,6,7,12,890,7,2,3,30); print(l1); print(l2); } public static void print(ArrayList<?> arrayList){ System.out.println(arrayList); }
Upper limit of generics:
? extends E: indicates E type or any subclass of E type
Lower bound of generics:
? super E: represents E type or any parent of E type