Generic T and T in Java? What's the difference? On type variables and wildcards

Generic T and T in Java? Differences between

T-type variables and? Wildcard difference

  1. Different definitions: T is a type variable,? Is a wildcard

  2. Different scope of use:

    1. ? Wildcards are used as parameter types, field types, local variable types, and sometimes as return types (but avoid doing so)
    2. T is used as the type parameter of the declaration class and the type parameter of the general method (note that type parameter and parameter type are two concepts here)
  3. Usually we use? I don'T know or care about the type at this time. I just want to use its general method, and? Wildcards are type parameters that cannot be applied to declared classes. They generally apply to methods and parameters. The type variable T is more widely used in class definition.

  4. To some extent? Wildcards and T parameter types are equivalent, but T parameter types do not support lower bound constraints, that is, T super SomeTing, while wildcards support? super SomeThing

  5. If you want to write a general method and the logic of the method doesn't care about types, use it boldly? Wildcards are used for adaptation and restriction. If you need the scope type (which may be more obvious when operating on general array types) or declare the type parameters of the class, use T-type variables

  6. The type parameter defines a variable representing the scope type (for example, T), and the wildcard only defines a set of allowed types that can be used for generic types. Wildcard means "use any type here"

In the use of generics, we can often see such usage:

public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}
List<? extends Integer> intList = new ArrayList<>();
List<? extends Number>  numList = intList;  // OK. List<? extends Integer> is a subtype of List<? extends Number>
public interface GenericProgressiveFutureListener<F extends ProgressiveFuture<?>> extends GenericFutureListener<F> {
    void operationProgressed(F future, long progress, long total) throws Exception;
}

If you still have questions about its usage and concept, you might as well continue to read this article

Understand their concepts: Generic Types and Wildcards , and use.

Generic Types type variable

Generic types, i.e. T, F, K and V, are generic classes or interfaces parameterized by type, which can also be called type variables

A type variable can be any non primitive type: any class type, any interface type, any array type, or even another type variable

By convention, type parameter names are single uppercase letters.

The most common type parameter names are:

  • E - element (widely used by the Java collection framework)
  • K - key
  • N - number
  • T - type
  • V - value
  • S. U, V, etc. - type 2, 3, 4

usage

1. Declare a generic type - Generic Class:

When we want to operate on common Object types, we may think of using Object, but using Object cannot be checked during compilation, because Object is the parent class of all classes, which may cause us to incorrectly pass in String in another part of the code when we pass in Integer and can get Inerger

public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}

To avoid the above problems, we can choose to use type variables

public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

You can also use multiple type parameters

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {

    private K key;
    private V value;

    public OrderedPair(K key, V value) {
	this.key = key;
	this.value = value;
    }

    public K getKey()	{ return key; }
    public V getValue() { return value; }
}

2. Declare generic methods - generic methods:

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the scope of a type parameter is limited to the method that declares it. Allows static and non static generic methods, as well as generic class constructors.

The syntax of a generic method includes a list of type parameters that appear in angle brackets before the return type of the method. For static generic methods, the type parameter part must appear before the return type of the method.

public class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

public static <T> void printListT(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

A complete call is

JestTestMain.<String>printListT(names);

However, types can usually be omitted. The function used here is type inference

JestTestMain.printListT(names);

Bounded type parameter

At the same time, we can limit the type parameters through the extends keyword

For example, < T extensions Number > the generic parameters here restrict the inheritance from the Number class.

public static <T extends Number> void printListT(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

At the same time, Java also supports multiple qualifications, such as < T extends charsequence & comparable < T > & serializable >, but if the qualification contains classes, they need to be written in the front

public static <T extends CharSequence & Comparable<T> & Serializable> void printListT(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

Wildcards wildcards

The wildcard refers to?

In generic code,? Represents an unknown type. Wildcards can be used in a variety of situations:

As a type of parameter, field, or local variable, sometimes as a return type (but avoid doing so).

Wildcards are never used as type parameters for generic method calls, generic class instance creation, or supertypes.

usage

There are three types of wildcards:

1. Upper bound wildcard:? extend upper bound type

Such as list <? extends Number> public static void process(List<? extends Foo> list) { /* ... */ }

We can use the upper bound wildcard to relax the restrictions on variables

2. Unbounded wildcard:?

Such as list <? > This indicates a list of unknown types. Generally, there are two cases where unbounded wildcards are useful:

  1. You are writing methods that can be implemented using the functions provided in the Object class
  2. When code uses methods in generic classes that do not depend on type parameters. For example, list Size or list clear. In fact, class <? > This is so common because most methods in class < T > do not rely on t.

How to understand the meaning of this sentence? Let's take an example:

public static void printList(List<Object> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

The intention of printList is to print any type of list, but it does not achieve the goal. It only prints the list of object instances. It cannot print list < integer >, list < string >, list < double >, etc. because they are not subtypes of list < Object >.

An error will be reported during compilation.

Here we replace it with wildcards to run correctly

public class JestTestMain {
    public static void main(String[] args) {
        List<String> names= Lists.newArrayList();
        names.add("Zhang San");
        names.add("Zhang San 1");
        names.add("Zhang San 2");
        printList(names);
    }

    public static void printList(List<?> list) {
        for (Object elem : list)
            System.out.println(elem + " ");
        System.out.println();
    }
}

Print:

Zhang San 
Zhang San 1 
Zhang San 2 

One thing to understand here is that list < Object > and list <? > Not the same. You can insert object objects or any subclass objects into the list < Object >, but you can only add objects to the list <? > Insert a null value into the.

3. Lower bound wildcard:? super subclass

For example: <? super Integer>

Suppose you want to write a method to put Integer objects into a list. In order to maximize flexibility, you want this method to apply to list < Integer >, list < number > and list < Object > anything that can hold Integer values.

public static void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
}

Type Erasure

One thing we need to know is that the compiler clears all type parameters at compile time, that is, it will actually replace our type parameters.

Even so, we still have a reason to use generics

  • The Java compiler performs more stringent type checking on generic code at compile time.
  • Generics support programming types as arguments.
  • Generics can implement generic algorithms.

Generics have been introduced into the Java language to provide stricter type checking at compile time and support generic programming. To implement generics, the java compiler applies type erasure to:

  • If the type parameter is unbounded, replace all type parameters in the generic type with their boundaries or objects. Therefore, the generated bytecode contains only ordinary classes, interfaces, and methods.
  • Insert type conversions as necessary to maintain type safety.
  • Generate bridging methods to preserve polymorphism in extended generic types.

Type erasure ensures that no new classes are created for parameterized types; Therefore, generics do not incur runtime overhead.

Here are two examples

// Type before erasure
public class Pair<K, V> {

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey(); { return key; }
    public V getValue(); { return value; }

    public void setKey(K key)     { this.key = key; }
    public void setValue(V value) { this.value = value; }

    private K key;
    private V value;
}
// After type erase
public class Pair {

    public Pair(Object key, Object value) {
        this.key = key;
        this.value = value;
    }

    public Object getKey()   { return key; }
    public Object getValue() { return value; }

    public void setKey(Object key)     { this.key = key; }
    public void setValue(Object value) { this.value = value; }

    private Object key;
    private Object value;
}

// Type before erasure
public static <T extends Comparable<T>> int findFirstGreaterThan(T[] at, T elem) {
    // ...
}
// After type erase
public static int findFirstGreaterThan(Comparable[] at, Comparable elem) {
    // ...
}

Finally, interested students can refer to:

Keywords: Java

Added by khendar on Fri, 07 Jan 2022 09:25:25 +0200