480 likes | 664 Views
Generics. Introduction. In any nontrivial software project, bugs are simply a fact of life. Careful programming & testing can help reduce their pervasiveness, but somehow, somewhere, they'll always find a way to creep into your code. Fortunately, some bugs are easier to detect than others. .
E N D
Generics M. Shoaib Farooq
Introduction • In any nontrivial software project, bugs are simply a fact of life. • Careful programming & testing can help reduce their pervasiveness, but somehow, somewhere, they'll always find a way to creep into your code. • Fortunately, some bugs are easier to detect than others. M. Shoaib Farooq
Compile-time bugs vs. Runtime bugs • Compile-time bugs, for example, tell you immediately that something is wrong; you can use the compiler's error messages to figure out what the problem is and fix it, right then and there. • Runtime bugs, however, can be much more problematic; they don't always surface immediately. Generics add stability to your code by making more of your bugs detectable at compile time. M. Shoaib Farooq
What are Java generics • JDK 1.5 introduces several extensions to the Java programming language. One of these is the introduction of generics. • Java Generics are a language feature that allows for definition and use of generic types and methods. • Generics are classes or interfaces that can be instantiated with a variety of types. • They have 1 or more formal type parameters • Using a generic you specify an actual type M. Shoaib Farooq
What are Java generics • Generic types or methods differ from regular types and methods in that they have type parameters. • Examples of generic types can be found in the collection framework of the J2SE 5.0 platform libraries. M. Shoaib Farooq
Example • A class like LinkedList<E> is a generic type. • It has a type parameter E that represents the type of the elements stored in the list. • We can use a LinkedList<String> or a LinkedList<Integer>, thereby specifying that we mean a list of strings or integral values respectively. M. Shoaib Farooq
Type Parameter : “A place holder for a type argument” • Type Argument: “Type that replace the formal type parameters” LinkedList <String > list = new LinkedList<String>(); LinkedList <Integer > list = new LinkedList<Integer>(); Instantiations, such as LinkedList<String> or a LinkedList<Integer>,are called parameterized types, and String and Integer are the respective actual type arguments. M. Shoaib Farooq
Usage of Non Generic Collection • Prior to the JDK 5.0 release, when you created a Collection, you could put any object in it. Vector myVector= new Vector(); myVector.addElement(new Integer(10)); myVector.addElement ("Hello, World"); • Getting items out of the collection required you to use a casting operation: Integer myInt = (Integer) myVector.elementAt(0); String myString = (String) myVector.elementAt(1); M. Shoaib Farooq
Usage of Non Generic Collection • If you accidentally cast the wrong type, the program would successfully compile, but an exception would be thrown at runtime. Vector myVector= new Vector(); myVector.addElement(new Integer(10)); …….// other code Integer myInt = (Integer) myVector.elementAt(0); String myString = (String) myVector.elementAt(0); Compiles Safely but FAILS at Runtime Cast Required M. Shoaib Farooq
Generics • J2SE 5.0 provides compile-time type safety with the Java Collections framework through generics • Generics allows you to specify, at compile-time, the types of objects you want to store in a Collection. Then when you add and get items from the list, the list already knows what types of objects are supposed to be acted on. • So you don't need to cast anything. The "<>" characters are used to designate what type is to be stored. If the wrong type of data is provided, a compile-time exception is thrown. M. Shoaib Farooq
Generics (cont’d) • Example: import java.util.*; public class First { public static void main(String args[]) { List<Integer> myList = new ArrayList<Integer>(10); myList.add(10); // OK myList.add("Hello, World"); // OK ??? } } M. Shoaib Farooq
Generics (cont’d) • Example (compile-time exception): import java.util.*; public class First { public static void main(String args[]) { List<Integer> myList = new ArrayList<Integer>(10); myList.add(10); // Autoboxing converted the int type to an Integer myList.add("Hello, World"); } } First.java:7: cannot find symbol symbol : method add(java.lang.String) location: interface java.util.List<java.lang.Integer> myList.add("Hello, World"); ^ 1 error M. Shoaib Farooq
Benefits of generics • Making code better • Runtime: No ClassCastException • Compile time: Compiler discovers wrong uses of types. • Making code more flexible • Generic classes can be instantiated with different types. M. Shoaib Farooq
Type Safety In Java, a program is considered type-safe if it • compiles without errors and warnings • does not raise any unexpected ClassCastException at runtime. • Using a parameterized type such as LinkedList<String>, instead of LinkedList, enables the compiler to perform more type checks and requires fewer dynamic casts. • This way errors are detected earlier, in the sense that they are reported at compile-time by means of a compiler error message rather than at runtime by means of an exception. M. Shoaib Farooq
Consider the example of a LinkedList<String>. The type LinkedList<String> expresses that the list is a homogenous list of elements of type String. • Based on the stronger information the compiler performs type checks in order to ensure that a LinkedList<String> contains only strings as elements. • Any attempt to add an alien element is rejected with a compiler error message. M. Shoaib Farooq
Example (using a parameterized type): LinkedList<String> list = new LinkedList<String>(); list.add("abc"); // fine list.add(new Date()); // error M. Shoaib Farooq
Example (using a non-parameterized type): LinkedListlist = new LinkedList(); list.add("abc"); // fine list.add(new Date()); // fine as well • A non-parameterized list is a sequence of elements of type Object. M. Shoaib Farooq
Example (using a parameterized type): • Since it is ensured that a LinkedList<String> contains strings it is not necessary to cast an element retrieved from the list to type String. LinkedList<String> list = new LinkedList<String>(); list.add("abc"); String s = list.get(0); // no cast needed M. Shoaib Farooq
Same example (using a non-parameterized type): • Using a plain LinkedList, there is no knowledge and no guarantee regarding the type of the element retrieved. • All retrieval methods return an Object reference, which must be cast down to the actual type of the element retrieved. • LinkedList list = new LinkedList(); • list.add("abc"); • String s = (String)list.get(0); // cast required M. Shoaib Farooq
definition of a generic type • definition of a generic method • type parameters • type parameter bounds • type arguments • wildcards • wildcard bounds • instantiation of a generic type • raw type • concrete instantiation • wildcard instantiation • instantiation of a generic method • automatic type inference • explicit type argument specification M. Shoaib Farooq
How to define a generic type? • Like a regular type, but with a type parameter declaration attached. • A generic type is a reference type that has one or more type parameters. • In the definition of the generic type, the type parameter section follows the type name. • It is a comma separated list of identifiers and is delimited by angle brackets. M. Shoaib Farooq
Example (of a generic type): class Pair<X,Y> { private X first; private Y second; public Pair(X a1, Y a2) { first = a1; second = a2; } public static <X,Y>void printPair(Pair<X,Y> pair) { System.out.println("("+pair.getFirst()+","+pair.getSecond()+")"); } public X getFirst() { return first; } public Y getSecond() { return second; } public void setFirst(X arg) { first = arg; } public void setSecond(Y arg) { second = arg; } } M. Shoaib Farooq
Instantiation of Generic Type public static void main(String o[]){ Pair<String,Long> limit1 = new Pair<String,Long>("maximum",124L); Pair<String,Date> limit2 = new Pair<String,Date>("Current Date",new Date()); Pair.printPair(limit1); Pair.printPair(limit2);} SEE Stack.java CONCRETE INSTANTIATIONS M. Shoaib Farooq
A Primitive Type Cannot be Plugged in for a Type Parameter!!! • The type plugged in for a type parameter must always be a reference type: • It cannot be a primitive type such as int, double, or char • However, now that Java has automatic boxing, this is not a big restriction. M. Shoaib Farooq
Bounded Type Parameters • There may be times when you'll want to restrict the kinds of types that are allowed to be passed to a type parameter. • For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. • This is what bounded type parameters are for. M. Shoaib Farooq
Bounded Type Parameters • To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound, which in this example is Number. Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces). M. Shoaib Farooq
Generic Methods “A method with type parameters” • Not only types can be generic, but methods can be generic, too. • Static and non-static methods as well as constructors can have type parameters. • The syntax for declaration of the formal type parameters is similar to the syntax for generic types. • The type parameter section is delimited by angle brackets and appears before the method's return type. Its syntax and meaning is identical to the type parameter list of a generic type. M. Shoaib Farooq
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; } } • The max method has one type parameter, named A. It is a place holder for the element type of the collection that the method works on. The type parameter has a bound; it must be a type that is a subtype of Comparable<A>. M. Shoaib Farooq
Generic Methods static <T> void fromArrayToCollection(T[] a, Collection<T> c) { for (T o : a) { c.add(o); // correct } } • See M. Shoaib Farooq
Subtyping It's possible to assign an object of one type to an object of another type provided that the types are compatible. For example, you can assign an Integer to an Object, since Object is one of Integer's supertypes: Object someObject = new Object(); Integer someInteger = new Integer(10); someObject = someInteger; // OK In object-oriented terminology, this is called an "is a" relationship. Since an Integer is a kind of Object, the assignment is allowed M. Shoaib Farooq
Subtyping But Integer is also a kind of Number, so the following code is valid as well: public void someMethod(Number n){ // method body omitted } someMethod(new Integer(10)); // OK someMethod(new Double(10.1)); // OK M. Shoaib Farooq
Subtyping • The same is also true with generics. You can perform a generic type invocation, passing Number as its type argument, and any subsequent invocation of add will be allowed if the argument is compatible with Number: Vector<Number> vector = new Vector<Number>(); vector.addElement(new Integer(10)); // OK vector.addElement(new Double(10.1)); // OK M. Shoaib Farooq
Subtyping void printAll(Vector<Object> c) { for (Object o : c) System.out.println(o); } Vector<String> list = new Vector<String>(); ... fill list ... printAll(list); // error M. Shoaib Farooq
Subtyping Is Vector<Object> a supertype of Vector<String>? No, different instantiations of the same generic type for different concrete type arguments have no type relationship. M. Shoaib Farooq
Subtyping • In general, if Foo is a subtype (subclass or subinterface) of Bar, and G is some generic type declaration, it is not the case that G<Foo> is a subtype of G<Bar>. M. Shoaib Farooq
Wildcard parameterized type • They do not have concrete types as type arguments, but so-called wildcards. • A wildcard is a syntactic construct with a"?" that denotes not just one type, but a family of types. In its simplest form a wildcard is just a question mark and stands for "all types". • A wildcard describes a family of types. There are 3 different flavors of wildcards: • "?" - the unbounded wildcard. It stands for the family of all types. • "? extends Type " - a wildcard with an upper bound. It stands for the family of all types that are subtypes of Type, type Type being included. • "? super Type " - a wildcard with a lower bound. It stands for the family of all types that are super types of Type, type Type being included. M. Shoaib Farooq
WildCards • Consider the problem of writing a routine that prints out all the elements in a collection. Here’s how you might write it in an older version of the language: void printCollection(Collection c) { Iteratori = c.iterator(); for (k = 0; k < c.size(); k++) { System.out.println(i.next()); }} M. Shoaib Farooq
WildCards And here is a naive attempt at writing it using generics (and the new for loop syntax): void printCollection(Collection<Object> c) { for (Object e : c) { System.out.println(e); }} M. Shoaib Farooq
WildCards • The problem is that this new version is much less useful than the old one. • Whereas the old code could be called with any kind of collection as a parameter, the new code only takes collection<Object>, which, as we’ve just demonstrated, is not a supertype of all kinds of collections! So what is the supertype of all kinds of collections? • Its written Collection<?> (pronounced “collection of unknown”), that is, a collection whose element type matches anything. It’s called a wildcard type for obvious reasons. M. Shoaib Farooq
WildCards We can write: void printCollection(Collection<?> c) { for (Object e : c) { System.out.println(e); } } Now, we can call it with any type of collection. Notice that inside printCollection(), we can still read elements from c and give them type Object. M. Shoaib Farooq
Type Erasure • When a generic type is instantiated, the compiler translates those types by a technique called type erasure “ a process where the compiler removes all information related to type parameters and type arguments within a class or method” • Type erasure enables Java applications that use generics to maintain binary compatibility with Java libraries and applications that were created before generics. M. Shoaib Farooq
Type Erasure public class MyClass<E> { public static void myMethod(Object item) { if (item instanceof E) { //Compiler error ... } E item2 = new E(); //Compiler error E[] iArray = new E[10]; //Compiler error E obj = (E)new Object(); //Unchecked cast warning } } M. Shoaib Farooq
Type Erasure • The operations shown in bold are meaningless at runtime because the compiler removes all information about the actual type argument (represented by the type parameter E) at compile time. M. Shoaib Farooq
Type Erasure • Type erasure exists so that new code may continue to interface with legacy code. Using a raw type for any other reason is considered bad programming practice and should be avoided whenever possible. • When mixing legacy code with generic code, you may encounter warning messages similar to the following: Note: WarningDemo.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. M. Shoaib Farooq
Warning Demo class Box<T> { private T t; // T stands for "Type" public void add(T t) { this.t = t; } public T get() { return t; } } M. Shoaib Farooq
WarningDemo public class WarningDemo { public static void main(String[] args){ Box<Integer> bi; bi = createBox(); } static Box createBox(){ return new Box(); } M. Shoaib Farooq
WarningDemo Recompiling with -Xlint: unchecked reveals the following additional information: WarningDemo.java:4: warning: [unchecked] unchecked conversion found : Box required: Box<java.lang.Integer> bi = createBox(); ^ 1 warning M. Shoaib Farooq
Why do instantiations of a generic type share the same runtime type? Because of type erasure • The compiler translates generic and parameterized types by a technique called type erasure. • Basically, it elides all information related to type parameters and type arguments. For instance, the parameterized type List<String> is translated to type List, which is the so-called raw type. • The same happens for the parameterized type List<Long>; it also appears as List in the bytecode. • After translation by type erasure, all information regarding type parameters and type arguments has disappeared. As a result, all instantiations of the same generic type share the same runtime type, namely the raw type. M. Shoaib Farooq