Generic types and wildcards

Generic types

We create generic methods when we do not want to define many different methods for every possible type of argument. The class in which those methods occur must also be specified as a generic type. Inside the <> signs after the class name and before the method name, we create a custom name(s) for the data type(s). Inside the class, we replace all data type(s) with the one(s) specified earlier (they will substitute themselves with the arguments, which are, of course, data types).


public class Main {
    public static void main(String[] args) {
        generalTypeClass<String> x = new generalTypeClass<>();
        x.variable = "x";
        System.out.println(x.returnValue());

        generalTypeClass<Integer> y = new generalTypeClass<>();
        y.variable = 4;
        System.out.println(y.returnValue());

        generalTypeClass2<Integer, Integer> z = new generalTypeClass2<>();
        z.variable = 4;
        z.variable2 = 5;
        System.out.println(z.returnValue());
        System.out.println(z.returnValue2());
    }
}

class generalTypeClass <TYPE> {
    TYPE variable;
    public TYPE returnValue() {
        return this.variable;
    }
}

class generalTypeClass2  <TYPE, TYPE2> {
    TYPE variable;
    TYPE2 variable2;
    public TYPE returnValue() {return this.variable;}
    public TYPE2 returnValue2() {return this.variable2;}
}
                                    

Wildcards

Generic types define classes, interfaces, or methods that can operate on various data types while preserving type safety at compile time. Wildcards make generic types more flexible, often appearing in method parameters to only allow subtypes (? extends T) or supertypes (? super T) of a specified type. Wildcards enable flexibility when an exact type does not need to be specified. In contrast to generics, which define specific types, wildcards allow for broader, more flexible applications.


import java.util.*;

public class Main {
    public static void main(String[] args) {
        ArrayList<SuperClass> list = new ArrayList<>();
        list.add(new SuperClass());
        list.add(new SubClass());

        ArrayList<SubClass> list2 = new ArrayList<>();
        SuperClass.method(list);
        SuperClass.method(list2); // this will work because SubClass is a subclass of SuperClass (extends SuperClass)
    }
}

class SuperClass {
    public static void method(List<? extends SuperClass> x){}
}

class SubClass extends SuperClass{}