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