Java generics

Personal Technology Blog (IBLi)

CSDN making the nuggets

1. Generic definitions

Program code written using generics is more secure and readable than code that uses Object variables haphazard and then casts them. — Java Core Technology

Generics come in at compile time;

Generic variables are capitalized. In Java libraries, the variables E represent the element type of the collection, K and V represent the key and value types of the table.

2. Wildcard

2.1 Borderless Wildcards

The unbounded wildcard becomes the unqualified wildcard

public static void main(String[] args) {
        List<String> list1 = new ArrayList<>();
        list1.add("1");
        list1.add("2");
        list1.add("3");
        list1.add("4");
        loop(list1);
    }

    public static void loop(List<?> list) {
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
Copy the code

2.2 Upper boundary wildcards

Upper – and lower-bound wildcards are both qualified wildcards

Public static void main(String[] args) {//List <Integer> list1 = new ArrayList<>(); list1.add(1); list1.add(2); list1.add(3); list1.add(4); loop(list1); } public static void loop(list <? extends Number> list) { for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); }}Copy the code

? Extends Number Extends Number if there are more than one qualified type, it is separated by an &

2.3 Lower boundary Wildcard

Public static void main(String[] args) {//List <Number> list1 = new ArrayList<>(); list1.add(1); list1.add(2L); list1.add(new BigDecimal(22)); list1.add(4); loop(list1); } @param list */ public static void loop(list <? super Number> list) { for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); }}Copy the code

3. Use of generics

Generics must be declared before they are used, otherwise there will be compilation errors; The declaration of generics is done with a pair of <>, and the convention is to use a capital letter; Wildcards cannot be used as return values;

Public <T> T testA(T T, Test1<T> Test1) {system.out.println (" this is the pass T:" + T); t = test1.t; System.out.println(" this is T:" + T); return t; }Copy the code
  • To get data from a generic class, use extends;
  • To write data to a generic class, use super;
  • To both take and write, wildcards are not used (that is, neither extends nor super).

3.1 a generic class

public class Demo<K, V> { public <K> K test(V v) { return null; }}Copy the code

3.2 Generic methods

public class DemoTest4<K, V> {/** ** <T> represents the declaration of generics ** @param T the declaration of generics * @param <T> the declaration of generics * @return */ public <T> T test(T T) {return null; } @param <X> public <X> X aa(k k) {return (X) null; ** @return */ public static <X> X bb() {return null; }}Copy the code

3.3 Generic Interfaces

Let’s start with a Demo that doesn’t use generic interfaces

Public interface IGeneric {Integer aa(Integer a); Integer bb(Integer b); } Then create a class to implement the method: public class implements IGeneric{@override public Integer aa(Integer a) {return null; } @Override public Integer bb(Integer b) { return null; }}Copy the code

The above interface design does not use generics, but aa method operation type is equivalent to writing dead in the interface, if we need a String aa method at this time, it is not necessary to declare a String type interface, and then to implement it, it is not bloated code, code duplication; So we can take a look at what happens when we use generics.

IGenericInte<T> {T aa(T a); T bb(T b); } Public class IGenericInteger implements IGenericInte<Integer> {@override public Integer aa(Integer a) { return null; } @Override public Integer bb(Integer b) { return null; Public String implements IGenericInte<String> {@override public String aa(String a) { return null; } @Override public String bb(String b) { return null; }}Copy the code

4. Generic erase

There are no generic type objects on the virtual machine; all objects belong to ordinary classes. When Java handles a generic type, it handles a corresponding primitive type. Erases the type variable and replaces it with a qualified type. If there is no qualified type, Object is used by default. If there are more than one qualified type, the first qualified type is used instead.

public interface IGenericInte<T> {
    T aa(T a);
    T bb(T b);
}
Copy the code

Like T, which is an unqualified variable, the generic erasure is replaced directly with Object. Of course, when a generic method is called, if the return type is erased, the compiler inserts a cast

Pair<Employee> buddies = ....
Employee buddy = buddies.getFirst();
Copy the code

Erasing the return type of getFirst returns the Object type. The compiler automatically inserts the Employee cast, which means that the compiler calls the method by executing two virtual machine instructions:

  • A call to the original method pair.getFirst () method
  • Cast the returned Object type to Employee
public static <T extends Comparable> T foo(T [] args)
Copy the code

After erasing the type, it becomes:

public static Comparable T foo(Comparable [] args)
Copy the code

The argument type T has been erased, leaving only the qualifying type Comparable;

Summary of facts about Java generic transformations:

  • Virtual machines have no generics, only ordinary classes and methods
  • All type parameters are replaced with their qualified types
  • The == bridge method is synthesized to guarantee polymorphic ==
  • To preserve type safety, cast casting is inserted when necessary

The first one should be fairly straightforward, which is why generic erasure is a concept, because the JVM cannot manipulate generics; The second is to explain how generics erase types; The third is that the generic approach may contradict the idea of polymorphism, so use the bridge approach to transition or compatibility; As mentioned in article 4 above, casts can occur;

5. Limitations and limitations of generics

Of course, generics are not perfect in Java. They can solve problems such as code structure reuse, but there are some limitations. Here are my conclusions based on Java Core Technology:

5.1 Type parameters cannot be instantiated using underlying data types

The reason is that after type erasure, Object cannot store the value of the basic data type if the primitive type Object is used. So it can only be declared by its wrapper type;

5.2 The runtime query type applies only to the original type

public class DemoTest5<T> { public static void main(String[] args) { DemoTest5<String> demoTest5 = new DemoTest5<>(); DemoTest5<Integer> demoTest4 = new DemoTest5<>(); System.err.println(demoTest4.getClass().equals(demoTest5.getClass())); }}Copy the code

Demotest4.getclass ().equals(demotest5.getClass ())) = demoTest5;

class com.ibli.javaBase.generics.DemoTest5
Copy the code

So here’s a very classic interview question, how to determine what a generic is, and here we can use reflection to get the specific type of a generic;

5.3 Cannot create arrays of parameterized types

An Array of parameterized type will become Object[] after its type is erased. If you try to store a String element, an array-storeException will be thrown. The main purpose is still to protect the array security, can refer to several articles:

1. If Java doesn’t support Arrays of parameterized types, what does arrays.aslist () do with them? 2, Java cannot create generic array of parameterized type 3, Java. Lang. ArrayStoreException

5.4 Varargs warning

Pass an instance of a generic type to a method with a variable number of arguments, and the compiler will warn you! There are two ways to suppress this warning:

  • Adding an annotation to calling methods @SuppressWarnings(” Unchecked “)
  • You can also annotate methods directly using @Safevarargs annotations

Reference Java cannot create generic arrays of parameterized types

5.5 Type variables cannot be instantiated

Cannot use new T(..) Or new T […]. And type variables of expressions like t. class; Because when the type is erased, T becomes Object, obviously we don’t want to create an Object instance here. The solution is to provide a constructor expression in the caller, which is implemented using the Supplier function:

public class Pair<T> { private T first; private T second; public T getFirst() { return first; } public void setFirst(T first) { this.first = first; } public T getSecond() { return second; } public void setSecond(T second) { this.second = second; } public Pair(T first, T second) { this.first = first; this.second = second; } public static <T> Pair<T> build(Supplier<T> constr) { return new Pair<>(constr.get(), constr.get()); } /** * Cannot infer type arguments for Pair2<> * @param c1 * @return */ public static <T> Pair<T> build(Class<T> c1){ try { return new Pair<>(c1.newInstance(),c1.newInstance()); } catch (InstantiationException | IllegalAccessException e) { return null; }}}Copy the code

Supplier is a function interface that returns a function of type T with no arguments:

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
Copy the code
public class TestMakePair { public static void main(String[] args) { /** * 1. Accept Supplier<T>-- it is a functional interface. Represents a function with no arguments and return type T. * Public Pair() {first = new T(); second = new T(); } * So the best way is to have the caller provide a constructor expression. * @param constr * @return */ Pair<String> Pair = Pair. Build (String::new); System.out.println(pair.getFirst().length()); /** * public void buildT(){ 2. The traditional way to construct generic objects is through the class.newinstance method. But t.glass is illegal because the details are so complicated. Illegal class literal for the type parameter T t.clast.newinstance (); */ Pair<String> pair1 = */ Pair<String> pair1 = */ Pair<String> pair1 = Pair.build(String.class); System.out.println(pair1.getFirst().length()); }} Result: 0 0Copy the code

5.6 Generic arrays cannot be constructed

Just as you cannot instantiate a generic instance, you cannot instantiate an array. Arrays themselves also have types that are used to monitor arrays stored in the JVM. This type is erased, for example:

public static <T extends Comparable> T[] foo(T[] a){ T[] mm = new T[2]; . }Copy the code

Type erasure causes this method to construct the Comparabel[2] array forever;

5.7 Invalid type variables in the static context of generic classes

This should be easier to understand. As mentioned above, generic types are applied to generic classes. Some static methods or static properties cannot use variable types of generic classes, and the compiler will directly report an error.

5.8 You cannot throw or catch instances of generic classes

Java can neither throw nor catch generic class objects; in fact, it is illegal even to extend a generic class Throwable.

public static <T extends Throwable> void doWork(Class<T> t){ try{ ... }catch (T ex){catch cannot be caught here the specific exception must be caught.... }}Copy the code

The use of type variables is allowed in the exception specification as follows:

public static <T extends Throwable> void doWork(Class<T> t) throws T { try{ ... }catch (Throwable ex){ t.initCause(ex); throw t; }}Copy the code

5.9 Can eliminate the check of checked anomalies

Java exception handling requires a single handler for all checked exceptions, but with generics, this can be circumvented;

@SuppressWarnings("unchecked")
public static <T extends Throwable> void throwAs(Throwable e) throws T{
    throw (T)e;
}
Copy the code

Calling the above method, the compiler considers t to be an unchecked exception;

5.10 Pay attention to conflicts after erasure

For example, if the equals method of a generic class is erased, it conflicts with the equals of Object. The solution is to rename the method that caused the error;

6. Generic inheritance

If Manage extends Employee, is Pair< Manage > a subclass of Pair< Employee >? No! But generic classes can extend or implement other generic classes. A typical example is ArrayList:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{}
Copy the code

ArrayList[E] extends AbstractList[E];

Some thoughts on Java generics

How does the compiler infer a specific type? Resources: An in-depth understanding of Java generics





— — — — — — — — — — — — — — — — — — — if someday if you cherish soaring aspirations Dare to smile huang chao uprising don’t husband — — — — — — — — — — — — — — — — — — —