Type System
Rules
Type Polymorphism
Generics
- Generics are parameterized types in Java.
- This allows types like Integer, String or user-defined types to be a parameter to methods, classes or interfaces.
- One of the ways of doing Generic Programming in Java (Java provides Metaprogramming capabilities too).
Types
- Generic classes
- Generic methods
- Generic interfaces
Defining
Naming convention
- T – Type
- E – Element
- K – Key
- N – Number
- V – Value
Classes
class Test<T> {
T obj;
Test(T obj) { this.obj = obj; }
public T useT() { return this.obj; }
}
Multiple types
class Test<T, U>{
T obj1;
U obj2;
Test(T obj1, U obj2){
this.obj1 = obj1;
this.obj2 = obj2;
}
}
Methods
class Test {
static <T> void genericMethod(T element){
// element...
}
Usage
-
Generics work only with reference types
Test<int> obj = new Test<int>(20); //compile time error
-
They work with primitive arrays since those are also references.
ArrayList<int[]> a = new ArrayList<>();
-
Generic types maintain type safety. Different types behave differently. For example, you can't assign a generic initialized with
Integer
to a generic initialized withString
.
Generic Programming#Compile time checks
Bounded Types
Allows to place constraints on generic types.
- Single bound
public class Calculator<T extends Number> { public double square(T number) { return number.doubleValue() * number.doubleValue(); } }
- Multiple bounds
public class Utility<T extends Comparable<T> & Serializable> { public void print(T obj) { System.out.println(obj); } }
Wildcard Types
- Unbounded wildcards (
?
): Represents any type - when type parameter doesn't matterpublic class Utility<T extends Comparable<T> & Serializable> { public void print(T obj) { } }
- Upper-bounded: Specifies the parent class - type parameter can be subclass of the specified bound
public double sum(List<? extends Number> list) { for (Number num : list) { } //... }
- Lower-bounded: Specifies the child class - type parameter can be super class of the specified bound
public void addElements(List<? super Integer> list) { list.add(1); list.add(2); }
Type Conversion/Casting
Boxing Unboxing
- Conversion from Wrapper class to primitive type and vice-versa
- Syntax
- No notation required
Primitive conversion
Type Systems#Narrowing Conversion
Syntax
Need to explicitly state that we're fine with the resulting loss of information
float b;
int a = (int) b;
//show(int a)
show((int)b);
Type Systems#Widening Conversion
Syntax
No notation required
Why? Moving to larger primitive types - does not loose any information.
int a = 34;
float b = a;
double c = b;
Numeric Promotion in inter-type operations
It is necessary both numeric operands are compatible in size in an operation. If not, they're converted.
Here's how that happens (Rules)
- If one operand is double, float or long the other promoted to double, float or long respectively
- Else both are considered int
Object Type Conversion
Type Systems#Upcasting
Type Systems#Downcasting
Syntax
-
Same as narrowing conversion in primitive data types
float a = 10.5; int b = (int) a; print(a) -→ 10.5 print(b) -→ 10 Animal animal = new Animal(); Cat cat = (Animal) animal;
-
Another way
Animal animal = new Animal(); if (Cat.class.isInstance(animal)) { Cat cat = Cat.class.cast(animal); }
###### [[Issues#Class Cast Exception|ClassCastException]]
That is why it is advised to use *instanceOf* check before downcasting.
- instanceOf
Used to avoid the ClassCastException by checking if the object belongs to a certain type
```java
//Example
if (animal instanceof Cat) {
((Cat) animal).meow();
}
//case 1
Dog dog = new Dog();
Animal animal = dog;
if(Cat.class.instanceOf(animal)) -→ false
//case 2
Cat cat = new Cat();
Animal animal = cat;
cat.meow();
Cat.class.instanceOf(animal) -→ true
String Conversion
-
Conversion from primitive type to String
-
Done through lang.java.lib.classes.wrappers which override the toString() method
-
Syntax
Integer a = 130; String s = a.toString();
-
Conversion from String to primitive type
-
Each wrapper class has a parse method → parseXXX()
-
Syntax
int a = Integer.parseInt(s); byte b = Byte.parseByte(s); //Exception //Since String is made of chars anyway //considering the String is made of a single character char c = s.charAt(0)
## Characteristics
- Type safety: [[Type Systems#Strong Typing]]
- Type checking: [[Type Systems#Static Type Checking]]
- Type Compatibility: [[Type Systems#Nominal Typing (Name-based Typing)]]
## References
- [Generics in Java - GFG](https://www.geeksforgeeks.org/generics-in-java/)