Generic
Advanced Programming
SUT • Spring 2019
Contents
-
Generic Methods
-
Generic Classes
-
Generics and Inheritance
-
Erasure
Generic Method
Example
public static void printArray(Integer[] intputArray) {
// display array elements
for (Integer element: inputArray)
System.out.println("%s", element);
} // end of printArray
// method printArray to print Double Array
public static void printArray(Double[] intputArray) {
// display array elements
for (Integer element: inputArray)
System.out.println("%f", element);
} // end of printArray
// method printArray to print Character Array
public static void printArray(Integer[] intputArray) {
// display array elements
for (Integer element: inputArray)
System.out.println("%c", element);
} // end of printArray
Stack interfaces
interface StringStack{
void push(String s);
String pop();
}
interface IntegerStack{
void push(Integer s);
Integer pop();
}
interface StudentStack{
...
}
Sort Method
static void sort(Integer[] array) {
// ...
}
static void sort(Double[] array) {
// ...
}
static void sort(String[] array) {
// ...
}
static void sort(Student[] array){
// ...
}
The Problem
- What is wrong with these examples?
- Code redundancy
- No effective code reuse
- Solution?
- Using Object class
- Pros and Cons?
- Compile-time type safety
The Solution
- Generic types and methods
- Methods with similar implementation
- Applicable for different parameters
Generic Methods
- Declaring a method which accepts different parameter types
- For each method invocation, the compiler searches the appropriate method
- If the compiler does not find a method, it looks for a compatible generic method
public static <E> void printArray(E[] intputArray);
Type Parameter
It says: In this method, E is not a regular type, it is a generic one
printArray() Generic Method
public static <E> void printArray(E[] inputArray){
// display array elements
for(E element: inputArray)
System.out.println("%s ", element);
System.out.println();
} // end method printArray
// Example
Integer[] integerArray = {1, 2, 3, 4, 5, 6, 7};
Double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7};
Character[] characterArray = {'H', 'E', 'L', 'L', 'O'};'
printArray(integerArray);
printArray(doubleArray);
printArray(characterArray);
Benefits of Generics
-
Restricting possible types
-
Compile-time type checking
-
printArray(stringArray) brings
-
Compiler Error
-
or exception?
-
public static <E extends Number> void printArray(E[] inputArray){…}
Type parameter as the Return Type
public static <T extends Comparable<T> > T maximum(T x, T y, T z){
T max = x; // assume x is initially the largest
if (y.compareTo(max) > 0) {
max = y; // y is the largest so far
}
if (z.compareTo(max) > 0) {
max = z; // z is the largest
}
return max; // return the largest object
}
System.out.println(maximum(3, 4, 5));
System.out.println(maximum(6.6, 7.7, 8.8));
System.out.println(maximum("pear", "apple", "orange"));
Stack Generic Interface
interface Stack<T>{
void push(T s);
T pop();
}
Stack<String> stringStack = new ...
stringStack.push(“salam”);
String s = stringStack.pop();
Stack Generic Implementation
public class Stack<E> {
private E[] elements ;
private final int size; // number of elements in the stack
private int top; // location of the top element
public void push(E pushValue) {
if (top == size - 1) // if stack is full
throw new FullStackException();
elements[++top] = pushValue;
}
public E pop() {
if (top == -1) // if stack is empty
throw new EmptyStackException();
return elements[top--];
}
public Stack() {
size = 10;
top = -1;
elements = (E[]) new Object[size];
}
}
Using Stack Class
Stack<String> stack1 = new Stack<String>();
stack1.push("first");
stack1.push("second");
System.out.println(stack1.pop());
System.out.println(stack1.pop());
Stack<Integer> stack2 = new Stack<Integer>();
stack2.push(1);
stack2.push(2);
System.out.println(stack2.pop());
System.out.println(stack2.pop());
public class Stack<E extends Student> {
private E[] elements ;
private final int size; // number of elements in the stack
private int top; // location of the top element
public void push(E pushValue) {
if (top == size - 1) // if stack is full
throw new FullStackException();
elements[++top] = pushValue;
}
public E pop() {
if (top == -1) // if stack is empty
throw new EmptyStackException();
return elements[top--];
}
public Stack() {
size = 10;
top = -1;
elements = (E[]) new Student[size];
}
}
Raw Types
-
Generic classes and methods can be used without type parameter
Stack<String> s = new Stack<String>(); // String as type parameter
s.push(“salam”);
s.push(new Integer(12)); // Compiler Error
Stack objectStack = new Stack(); // no type parameter
s.push(“salam”);
s.push(new Integer(12));
s.push(new Student(“Ali Alavi”));
No Generics in Runtime
- Generics is a compile-time aspect
- In runtime, there is no generic information
- All generic classes and methods are translated with raw types
- Byte code has no information about generics
- Only raw types in byte code
- This mechanism is named erasure
Erasure
- When the compiler translates generic method into Java bytecodes
- It removes the type parameter section
- It replaces the type parameters with actual types.
- This process is known as erasure
Erasure Example (1)
class Stack<T> {
void push(T s){...}
T pop() {...}
}
// Is translated to
class Stack {
void push(Object s){...}
Object pop() {...}
}
Erasure Example (2)
public static <T extends Comparable<T> > T maximum(T x, T y, T z);
// translated to:
public static Comparable maximum(Comparable x, Comparable y, Comparable z);
What Happens if…
public static <E extends Number> void f(E i){
}
public static void f(Number i){
}
// Compiler Error: Method f(Number) has the same erasure
// f(Number) as another method in this type
Generics and Inheritance
-
A non-generic class can be inherited by a non-generic class
-
As we saw before learning generics
-
-
A generic class can be inherited from a non-generic class
-
Adding generality to classes
-
-
A non-generic class can be inherited from a generic class
-
Removing generality
-
-
A generic class can be inherited by a generic class
Example
class GenericList<T> extends Object{
public void add(T t){...}
public T get(int i) {...}
public void remove(int i) {...}
}
class GenericNumericList<T extends Number> extends GenericList<T>{
}
class NonZeroIntegerList extends GenericList<Integer>{
public void add(Integer t) {
if(t==null || t==0)
throw new RuntimeException(“Bad value");
super.add(t);
}
}
Some Notes
-
We can also create generic interfaces
-
No primitives as type parameters
interface Stack<T>{
void push(T s);
T pop();
}
}
Multiple Type Parameters
class MultipleType<T, K>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public void doSomthing(K k, T t){…}
}
MultipleType<String, Integer> multiple =
new MultipleType<String, Integer>();
multiple.doSomthing(5, "123");
Note
-
You can not instantiate generic classes
-
-
-
-
Syntax Error: Cannot instantiate the type T
-
Why?
-
class Stack<T>{
T ref = new T();
}
Note (2)
-
You can not instantiate generic classes
-
-
-
-
Syntax Error: Cannot instantiate the type T
-
Why?
-
class Stack<T>{
T[] elements = new T[size];
}
Note (3)
-
You cannot create a generic array
-
-
-
-
-
Syntax Error: Cannot create a generic array of Box<String>
-
Why?
-
class Box<T> {
final T x;
Box(T x) {
this.x = x;
}
}
Box<String>[] bsa = new Box<String>[3];
Reason
-
Operations such as instanceof and new are runtime operations
-
They use a type at runtime
-
With erasure type information is removed at runtime
-
So these operations are Meaningless
-
Although, they may be possible
-
Generics and Java 7
-
Older versions:
-
ArrayList<String> list = new ArrayList<String>();
-
-
With Java 7:
-
ArrayList<String> list = new ArrayList<>();
-
-
Type information after new are ignored.
-
List<Map<Long, Set<Integer>>> list = new ArrayList<>();
-
Further Reading
-
Wildcards as type parameters
-
Java generics vs. C++ templates
-
Erasure is different in these languages
-
-
Type Argument inference
-
More on erasure
-
TIJ is so better than Deitel in generics chapter
-
More Depth
-
Wow!!!
public static void wow(ArrayList<String> list){
Method method = list.getClass()
.getMethod("add", Object.class);
method.invoke(list, new Integer(2));
}
public static void main(String args[]){
ArrayList<String> s = new ArrayList<String>();
wow(s);
for (Object string : s) {
System.out.println(string);
}
}
A Note on Inheritance
class A{
public Object f(Object o){
return new Object();
}
}
class B extends A{
public Object f(Object o){
return new String("salam");
}
}
// B.f() overrides A.f()
A Note on Inheritance
class A{
public Object f(Object o){
return new Object();
}
}
class B extends A{
public String f(Object o){
return new String("salam");
}
}
// B.f() overrides A.f()
A Note on Inheritance
class A{
public Object f(Object o){
return new Object();
}
}
class B extends A{
public Object f(String o){
return new String("salam");
}
}
// B.f() is overloading A.f()
// B.f() does not override A.f()
Pair<T, K>
class Pair<T,K>{
private T first;
private K second;
public Pair(T t, K k) {
this.first = t;
this.second = k;
}
public T getFirst() {
return first;
}
public K getSecond() {
return second;
}
public String toString() {
return "[" + second + ", " + first + "]";
}
}
Pair<T, K>
Pair<Integer, String> pair1 =
new Pair<Integer, String>(4, "Ali");
Integer i = pair1.getFirst();
String s = pair1.getSecond();
Pair<String, Boolean> pair2 =
new Pair<String, Boolean>("salam", true);
String ss = pair2.getFirst();
Boolean bb = pair2.getSecond();
equals() method
-
What is wrong with this implementation?
public boolean equals(Pair<T,K> pair) {
return pair.first.equals(first) &&
pair.second.equals(second);
}
boolean equals(Pair<T,K> pair)
-
It should check for nullity of pair
-
It should check for nullity of pair.first and pair.second
-
It should check for nullity of this.first and this.second
-
This method does not override equals()
-
It is overloading it
-
Correct signature:
-
boolean equals(Object pair)
-
What if parameter is not a Pair?
Type Checking
public boolean equals(Object o) {
Pair<T, K> pair = null;
try{
pair = (Pair<T, K>) o;
} catch(ClassCastException e){
return false;
}
return pair.first.equals(first) &&
pair.second.equals(second);
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Pair other = (Pair) obj;
if(first == null){
if(other != null)
return false;
}else if (!first.equals(other.first))
return false;
if(second == null) {
if (other.second != null)
return false;
} else if (!second.equals(other.second))
return false;
return true;
}
Title Text
Generic
By Behnam Hatami
Generic
Generic / Advanced Programming Course @ SUT, Spring 2019
- 1,241