Generic Programming seminar

Preview:

DESCRIPTION

 

Citation preview

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generic ProgrammingSeminar by - Gautam Roy - Debasish Sengupta

<T>

<E>

<K, V>

<T>

<E>

<K, V>

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Today’s Agenda

•Theoretical concept of Generic Programming.

•Generics in Java.•Questions are welcome throughout our

session.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

What is Generic Programming?

•Raising the level of abstraction.•What do we do when we program?

▫We write a sequence of statements.•What is the level of abstraction?

▫We write at the level of source code. That gets translated directly into machine code, which is less abstract.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

<T>

<E>

<K, V>

<T>

<E>

<K, V>

• With generic programming, the level of abstraction is higher. The source code is just a pattern.▫ The actual machine code that comes out depends on

how the pattern is “filled-in”. static <T> List<T> newList() { return new ArrayList<T>();}

▫ Without knowing T, no machine code can be generated from the above.

• Summary:▫ With normal programming, we can know the overview of

low level code generated from just by looking at the source.

▫ With generic programming, the code is at a higher level. Need to know other things, like the type parameters.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

•So…▫Generic programming is programming that

focuses on the algorithms and code at a higher level of abstraction that “normal” programming.

▫Generic programming is a style of computer programming in which algorithms are written in terms of to-be-specified-later types that are then instantiated when needed for specific types provided as parameters

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Path to Generic Programming

•Lifting: Lifting is the process of finding commonality among similar implementations of the same algorithm and then providing suitable abstractions so that a single, generic algorithm can cover many concrete implementations.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Path to Generic Programming(contd..)•Lifting is repeated until the generic

algorithm has reached a suitable level of abstraction, where it provides maximal reusability while still yielding efficient implementations.

•The abstractions themselves are expressed as requirements on the parameters to the generic algorithm.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

By-product of Generic Programming•Concept: A concept contains a set of

requirements that describe a family of abstractions, typically data types. Examples of concepts include Iterator, Graph, Comparable etc.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Format definition of Concept

•Once many algorithms within a given problem domain have been lifted, we start to see patterns among the requirements. It is common for the same set of requirements to be required by several different algorithms. When this occurs, each set of requirements is bundled into a concept.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

•Thus, the output of the Generic Programming process is not just a generic, reusable implementation, but a better understanding of the problem domain.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Lifting in Action• Consider the following two definitions of “max”.

▫ int max(int a, int b) { if (a > b) return a; else return b;}

▫ float max(float a, float b) { if (a > b) return a;

else return b; }

• How can we lift this to create just one version?<T> T max(T a, T b) { if (a > b) return a; else return b;}▫ Does it compile in Java latest version?

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Evolution of Concept

•T needs to be Comparable so that we can compare various types.

•When we will write “max” for n number of elements then we need Container and also Comparable.

- Here Comparable is a concept here.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Lifted version of “max”<T extends Comparable<? super T>> T max(T a, T b) {

Comparable c1 = (Comparable) a;Comparable c2 = (Comparable) b;if (c1.compareTo(c2) > 0) return a;else return b;

}

- This code introduce the concept Comparable.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generics and Polymorphism•One view of polymorphism is

▫Ability of code to work with different types •Generic parameters and subtyping

▫Both mechanisms for accomplishing polymorphism

▫Both support programming by abstracting from concrete types

•Polymorphism using generic types - parametric polymorphism

•Polymorphism using subtypes - subtype polymorphism

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generics in Java

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Original team behind Java Generics

Left to right: Philip Wadler, Martin Odersky, Gilad Bracha, Dave Stoutamire

Making Java easier to type, and easier to type

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generics in Java

•Use of raw type in JavaList l = new ArrayList(); // 1l.add(Integer.valueOf(0)); // 2Integer x = (Integer) l.get(0); // 3- Can you see the disadvantages of these

raw type?

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Use of generic type

•Same code with generics:List<Integer> l = new ArrayList<Integer>(); // 1l.add(Integer.valueOf(0)); // 2Integer x = l.get(0); // 3

•And with Autoboxing and unboxing: List<Integer> l = new ArrayList<Integer>(); // 1l.add(0); // 2int x = l.get(0); // 3

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Advantages of generic type// Generic type: GoodCollection<Coin> coinCollection = new ArrayList<Coin>();coinCollection.add(new Stamp()); // Won’t compile...for (Coin c : coinCollection) {...}

// Raw Type: EvilCollection coinCollection = new ArrayList();coinCollection.add(new Stamp()); // Succeeds but should not

...for (Object o : coinCollection) {Coin c = (Coin) o; // Throws exception at runtime...}

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Advantages of generic type ...

•Compile time error detection; more robust code

•Succinct and stylish coding practice.•Less typing and repetition; adheres to

DRY principle•Reduce Accidental Complexity in code.•Flexibility of API design.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generic Class and Constructorpublic class Pair<T, S> { private T first; private S second; public Pair(T f, S s) { first = f; second = s; } public T getFirst() { return first; }

public S getSecond() { return second; } public String toString() { return "(" + first.toString() + ", " + second.toString() + ")"; }}

- here T and S are type variable or formal type parameter

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generic class in Action

Pair<String, String> grade440 = new Pair<String, String>("mike", "A");Pair<String, Integer> marks440 = new Pair<String, Integer>("mike", 100); System.out.println("grade:" + grade440.toString()); System.out.println("marks:" + marks440.toString());

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generic interface

•Same as generic classpublic interface List<E> { void add(E x); Iterator<E> iterator();}

public interface Iterator<E> { E next(); boolean hasNext();}• Lots of example in Java collection framework like List, Set,

Map etc.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Type inference•The automatic deduction of the type

arguments of a generic method at compile time.

List<T> l = new ArrayList<T>();- Does it compile in Java latest version?

public static <T> newList() { return new ArrayList<T>;}- Does it compile in Java latest version?

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generic Methods• A generic method can be invoked in two

ways: Explicit type argument specification .  The type arguments are explicitly specified in a type argument list.

• Automatic type argument inference. The method is invoked like regular non-generic methods, that is, without specification of the type arguments.  In this case the compiler automatically infers the type

arguments from the invocation context. 

public <T> Pair<T,T> twice(T value) { return new Pair<T,T>(value, value); }

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generic method in Action• Explicit type argument:Pair<String, String> pair = this.<String>twice("Hello");

Or using type inferencePair<String, String> pair = twice("Hello");

• Explicit type argument System.out.println(Utilities.<String>max("abc","xyz"));

Or using type inference System.out.println(Utilities.max("abc","xyz"));

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Type parameter naming conventions•E - Element (used extensively by the Java

Collections Framework)

•K - Key

•N - Number

•T - Type

•V - Value

•S,U,V etc. - 2nd, 3rd, 4th types

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generics and Wildcards The method prints out all the elements in a

collection:void printCollection(Collection c) { Iterator i = c.iterator(); for (k = 0; k < c.size(); k++) { System.out.println(i.next()); }}

One naive attempt at writing it using generics:void printCollection(Collection<Object> c) { for (Object e : c) { System.out.println(e); }

} - Why it is inflexible?

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generics and Subtyping

•Arrays are covariant but generic types are contravariant.

•That is, List<String> is not a subtype of List<Object>

•Good for compile-time type safety, but inflexible

•Wildcard and bounded wildcard to the rescue

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Wildcards to the rescue

•Correct methodvoid printCollection(Collection<?> c) { for (Object e : c) { System.out.println(e); }}

- ? is wildcard type here.

•But what about this? Collection<?> c = new ArrayList<String>();c.add(new Object());

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Example of Subtypingpublic interface Shop<T> { T buy(); void sell(T myItem); void buy(int numToBuy, Collection<T> myCollection); void sell(Collection<T> myLot);}class Model { }class ModelPlane extends Model { }class ModelTrain extends Model { }

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Works Fine If You Stick to One Type// Individual purchase and saleShop<ModelPlane> modelPlaneShop = ... ;ModelPlane myPlane = modelPlaneShop.buy();modelPlaneShop.sell(myPlane);

// Bulk purchase and saleCollection<ModelPlane> myPlanes = ... ;modelPlaneShop.buy(5, myPlanes);modelPlaneShop.sell(myPlanes);

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Collection Subtyping Doesn’t Work!// You can't buy a bunch of models from the train

shopmodelTrainShop.buy(5, myModelCollection); // Won't compile

// You can't sell a bunch of trains to the model shopmodelShop.sell(myTrains); // Won't compilepublic interface Shop<T> { T buy(); void sell(T item); void buy(int numToBuy, Collection<T> myCollection); void sell(Collection<T> myLot);}

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Bounded Wildcards to the Rescuepublic interface Shop<T> { T buy(); void sell(T item); void buy(int numToBuy, Collection<? super T> myCollection); void sell(Collection<? extends T> myLot);}// You can buy a bunch of models from the train shopmodelTrainShop.buy(5, myModelCollection); // Compiles// You can sell your train set to the model shop;modelShop.sell(myTrains); // Compiles

Because

• List<String> is a subtype of List<? extends Object>• List<Object> is a supertype of List<? super String>

<T>

<E>

<K, V>

<T>

<E>

<K, V>

A Mnemonic for Wildcard Usage•PECS — Producer extends, Consumer

super• For a T producer, use Foo<? extends T>• For a T consumer, use Foo<? super T>

<T>

<E>

<K, V>

<T>

<E>

<K, V>

PECS example: Stack

•Suppose you want to add bulk methods to Stack<E>:

void pushAll(Collection<E> src);void popAll(Collection<E> dst);

- What we will get by PECS?

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Applying PECS to pushAll methods

void pushAll(Collection<? extends E> src);

– src is an E producervoid popAll(Collection<E> dst);

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Applying PECS to popAll methods

void pushAll(Collection<? extends E> src);

– src is an E producervoid popAll(Collection<? super E > dst);

– dst is an E consumer

<T>

<E>

<K, V>

<T>

<E>

<K, V>

What does it buy you?

void pushAll(Collection<? extends E> src);void popAll(Collection<? super E> dst);- Caller can now pushAll from a Collection<Long>

or a Collection<Number> onto a Stack<Number>- Caller can now popAll into a Collection<Object>

or a Collection<Number> from a Stack<Number>

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Advantages of generic type…

•Compile time error detection; more robust code

•Succinct and stylish coding practice.•Less typing and repetition; adheres to

DRY principle•Reduce Accidental Complexity in your

code.•Flexibility of API design.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Under the hood

•Generic code translation: By creating one unique byte code representation of each generic type (or method) and mapping all instantiations of the generic type (or method) to this unique representation.

•Type erasure: A process that maps a parameterized type (or method) to its unique byte code representation by eliding type parameters and arguments.

<T>

<E>

<K, V>

<T>

<E>

<K, V>

code translation strategy•Code specialization. The compiler generates a

new representation for every instantiation of a generic type or method. For instance, the compiler would generate code for a list of integers and additional, different code for a list of strings, a list of dates, a list of buffers, and so on. 

•Code sharing. The compiler generates code for only one representation of a generic type or method and maps all the instantiations of the generic type or method to the unique representation, performing type checks and type conversions where needed. 

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Generics is Evolution, Not Revolution

Java Generics uses Type erasure and non-Reification technique

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Steps of erasure and bridge methodThe steps performed during type erasure include:  Eliding type parameters.

When the compiler finds the definition of a generic type or method, it removes all occurrences of the type parameters and replaces them by their leftmost bound, or type Object if no bound had been specified. 

Eliding type arguments. When the compiler finds a parameterized type, i.e. an instantiation of a generic type, then it removes the type arguments. For instance, the types List<String> , Set<Long> , and Map<String,?> are translated to List , Set and Map respectively. 

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Example (before type erasure) interface Comparable <A> {

  public int compareTo( A that); } final class NumericValue implements Comparable <NumericValue> {   priva te byte value;    public  NumericValue (byte value) { this.value = value; }    public  byte getValue() { return value; }    public  int compareTo( NumericValue t hat) { return this.value - that.value; } } class Collections {    public static <A extends Comparable<A>>A max(Collection <A> xs) {     Iterator <A> xi = xs.iterator();     A w = xi.next();     while (xi.hasNext()) {       A x = xi.next();       if (w.compareTo(x) < 0) w = x;     }     return w;   } } final class Test {   public static void main (String[ ] args) {     LinkedList <NumericValue> numberList = new LinkedList <NumericValue> ();     numberList .add(new NumericValue((byte)0));      numberList .add(new NumericValue((byte)1));      NumericValue y = Collections.max( numberList );    } }

- Type parameters are green and type arguments are blue

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Example (after type erasure)interface Comparable {

  public int compareTo( Object that);} final class NumericValue implements Comparable { priva te byte value; 

 public  NumericValue (byte value) { this.value = value; }   public  byte getValue() { return value; }   public  int compareTo( NumericValue t hat)   { return this.value - that.value; }  public  int compareTo(Object that) { return this.compareTo((NumericValue)that);  }

} class Collections { 

public static Comparable max(Collection xs) {     Iterator xi = xs.iterator();     Comparable w = (Comparable) xi.next();     while (xi.hasNext()) {       Comparable x = (Comparable) xi.next();       if (w.compareTo(x) < 0) w = x;     }     return w; }

} final class Test {

public static void main (String[ ] args) {     LinkedList numberList = new LinkedList();     numberList .add(new NumericValue((byte)0));      numberList .add(new NumericValue((byte)1));      NumericValue y = (NumericValue) Collections.max( numberList );  }

}

- Bridge method is generated by Compiler for NumericValue

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Bridge method•A synthetic method that the compiler

generates in the course of type erasure.  It is sometimes needed when a type extends or implements a parameterized class or interface.

•The compiler insert bridge methods in subtypes of parameterized supertypes to ensure that subtyping works as expected.

•Programmer cannot invoke bridge method. 

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Converting Legacy code to generics

•Example of JDK APIpublic static <T extends Comparable<? super T>> T max(Collection<T> coll)

•This is fine, except that the erasure of this signature is

public static Comparable max(Collection coll)

•which is different than the original signature of max():

public static Object max(Collection coll)

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Conversion (contd..)

•Correct “max” for seamless running is legacy code:

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

•Multiple bound/conjunctive bound systax:<T extends T1& T2 ... & Tn>

<T>

<E>

<K, V>

<T>

<E>

<K, V>

References

•Effective Java , 2nd edition by Joshua Bloach.

• Java Generics and Collections By Maurice Naftalin, Philip Wadler

• JavaOne technical sessions.•http://www.generic-programming.org/•http://en.wikipedia.org/wiki/

Generics_in_Java•http://www.angelikalanger.com/

GenericsFAQ/JavaGenericsFAQ.html

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Q n A

<T>

<E>

<K, V>

<T>

<E>

<K, V>

Thank You

Recommended