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
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 example
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
-
If we create a superclass and a subclass, then we use
subclassName extends superclassName
-
If we create a interface and implement it in a class, then we use
className implements interfaceName
-
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
Wildcards
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
Here we fix the type of list be Vehicle, then we cannot setPrintValuse(busList)
since busList is List<Bus>
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) {
}
}
<? 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);
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 method
One more thing is generic type can have more type
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.
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