Skip to content

4.23 Self 8

Generic Class

Introduction

First let's see this example

public class Print { 
    Object value; 
    public Object getPrintValue(){ 
        return value;
    } 

    public void setPrintValue(Object value) {
        this.value = value;
    } 
}

We know print​ class is not a child class of other class, then by default it's a child class(sub class) of object​ since object​ class is a superclass of all the other classes

Note that setPrintValue​ accept the type Object​, which means it can accept any class object like Integer​, String​,.... because it is a parent class

public class Main {

    public static void main(String args[]) {

        Print printObj1 = new Print();
        printObj1.setPrintValue(1);//Here i am passing Integer 1
        Object printValue = printObj1.getPrintValue();
        //we can not use printValue directly, we have to typecast it, else it will be compile time error
        //Because it is a object, it can be anything, we need to typecast
        if (((int) printValue == 1)) {
            //do something
        }
    }
}

Continuing with comments above, if we code setPrintValue("Hello");​, then we need to typecast (String)printValue

Make use of Generic Classes

The syntaxes is Dam Diamond syntax <>

public class Print<T> {

    T value;

    public T getPrintValue() {
        return value;
    }

    public void setPrintValue(T value) {
        this.value = value;
    }
}

Here we use Dimond syntax <T>​ and T​ instead of Object

Generic Type (in above example ) can be any non-primitive object

public class Main {

    public static void main(String args[]) {

        Print<Integer> printObj1 = new Print<Integer>();
        printObj1.setPrintValue(1);
        Integer printValue = printObj1.getPrintValue();
        if (printValue == 1) {
            //do something
        }
    }
}

Then we no longer need typecasting

1. Non Generic Subclass

public class Print<T> {

    T value;

    public T getPrintValue() {
        return value;
    }

    public void setPrintValue(T value) {
        this.value = value;
    }
}

---

public class ColorPrint extends Print<String>{ 

}

2. Generic Subclass

public class Print<T> {

    T value;

    public T getPrintValue() {
        return value;
    }

    public void setPrintValue(T value) {
        this.value = value;
    }
}

---

public class ColorPrint<T> extends Print<T>{ //Then we don't need to specific the type

}

---

public class Main {

    public static void main(String args[]) {

        ColorPrint<String> colorPrintObj = new ColorPrint<>();
        //Note that ColorPrint<String> colorPrintObj = new ColorPrint<String>();
        //is also correct
        colorPrintObj.setPrintValue("2");
    }
}

3. More than one Generic Types Example

public class Pair<K, V> {

    private K key;
    private V value;

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

---

public class Main {

    public static void main(String args[]) {

        Pair<String, Integer> pairObj = new Pair<>();
        pairObj.put("hello", 1243);
    }
}

Generic Method

What if we only wants to make Method Generic, not the complete Class, we can write Generic
Methods too.

  • Type Parameter should be before the return type of the method declaration.
  • Type Parameter scope is limited to method only.
public class GenericMethod {

    public <K, V> void printValue(Pair<K, V> pair1, Pair<K, V> pair2) {
        if (pair2.getKey().equals(pair2.getKey())) {
            //do something
        }
    }
}

The syntax is like public <T> void setValue(T object ){}

Another exampleimage

image

Here is 9th-line, we can change the parameter to new Car()​ as well, since setVaule​ is a generic method

Raw Type

It's a name of the generic class or interface without any type argument.

public class Print<T> {

    T value;

    public T getPrintValue() {
        return value;
    }

    public void setPrintValue(T value) {
        this.value = value;
    }
}

---

public class Main {

    public static void main(String args[]) {

        Print<String> parametrizedTypePrintObject = new Print<>();
        //internally it passes Object as parametrized type.
        Print rawTypePrintObject = new Print();//Raw type, i didn't pass the type
        rawTypePrintObject.setPrintValue(1);
        rawTypePrintObject.setPrintValue("hello");
    }
}

Bounded Generics

It can be used at generic class and method since sometimes we want to bound the type of T

  • Upper Bound <T extends Number>​ means T can be of type Number or its Subclass only.

Here superclass (in this example Number) we can have interface too. * Multi Bound

Upper Bound
public class Print<T extends Number> {

    T value;

    public T getPrintValue() {
        return value;
    }

    public void setPrintValue(T value) {
        this.value = value;
    }
}

Here Number​ is a superclass in java like String​ which has the type: Integer, Double, BigInteger, BigDecimal,...

So now T​ only can be the type of Number can be

Notice that there are three similar things

  1. If we create a superclass and a subclass, then we use subclassName extends superclassName

  2. If we create a interface and implement it in a class, then we use className implements interfaceName

  3. If we create a generic class and bound it, then we use genericClassName<T extends BoundedClassName>

Now here is a correct example and followed by an incorrect example

//Correct
public class Main {

    public static void main(String args[]) {

        Print<Integer> parametrizedTypePrintObject = new Print<Integer>();
    }
}

---

//Incorrect
public class Main {

    public static void main(String args[]) {

        Print<String> parametrizedTypePrintObject = new Print<>();
    }
}
Multi Bound

Recall that Java don't allowed a subclass extends more than one superclass, but it do allow a class to implements more than one interface by override

Multi Bound:
<T extends Superclass & interface1 & interface N>

  • The first restrictive type should be concrete class
  • 2,3 and so on... can be interfaces.
public class A extends ParentClass implements Interface1, Interface2{

}

public class Print<T extends ParentClass & Interface1 & Interface2> {

    T value;

    public T getPrintValue() { return value; }

    public void setPrintValue(T value) { this.value = value; }
}

---
//This is allowed
public class Main {

    public static void main(String args[]) {

        A obj = new A();
        Print<A> printObj = new Print<>();
    }
}

//Now if class A be this
public class A extends ParentClass implements Interface1 {
}
//Then in the Main function it will have error since print class need to implement two interface

Wildcardsimage

Line 26 is valid because parent class Vehicle​ can keep the object of a child class Bus

Line 17 and 19 are not valid since list of vehicle is not a parent of list of bus also because vehiclelist can have object Bus and Car


Futhermore

image

Here we fix the type of list be Vehicle, then we cannot setPrintValuse(busList)​ since busList is List<Bus>​​image

How to solve this?

Using Wildcards

Wildcards:

  • Upper bounded wildcard: <? extends UpperBoundClassName>​ i.e. class Name and all its child class below

Example

import java.util.List;

public class Print {
    public void setPrintValues(List<? extends Vehicle> vehicleList) {

    }
}
* Lower bounded wildcard: <? Super LowerBoundClassName>​ i.e. class Name and all its super class above

import java.util.List;

public class Print {
    public void setPrintValues(List<? Super Vehicle> vehicleList) {

    }
}

---
//Then in the main function the following will works
List<object> objList = new Arraylist<>();
printObj.setPrintValues(objList);
printObj.setPrintValues(vehicleList);
//But this doesn't work
printObj.setPrintValues(busList);

image

image

From here we now the difference between generic type method and wild card method is that we can have different type

Another is we can use super keywords in wild card method but we cannot do this in generic type methodimage

One more thing is generic type can have more typeimage

Unbounded wildcard <?>​ only you can read

Use this when we know that our method can mostly work on your objects. All the methods available in the Object class are applicable, as we know that every class in Java is a subclass of the Object class.

image

Type Erasure

Generic Class Erasure

public class Print <T> {

    T value;

    public void setValue(T val) {
        this.value = val;
    }
}

---
//Byte code
//T will be replaced by object
public class Print {

    Object value;

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

Generic Class Bound Type Erasure

public class Print<T extends Number> {

    T value;

    public void setValue(T val) {
        this.value = val;
    }
}

---

public class Print {

    Number value;

    public void setValue(Number val) {
        this.value = val;
    }
}

Generic Method Erasure

public class Print {

    public <T> void setValue(T val) {
        System.out.println("do something");
    }
}

---

public class Print {

    public void setValue(Object val) {
        System.out.println("do something");
    }
}

Generic Bound Type Method Erasure

public class Print {

    public <T extends Bus> void setValue(T val) {
        System.out.println("do something");
    }
}

public class Print {

    public void setValue(Bus val) {
        System.out.println("do something");
    }
}

You create generic class, but the type code internally remove those