+ All Categories
Home > Documents > Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we...

Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we...

Date post: 19-Jan-2016
Category:
Upload: maximillian-preston
View: 224 times
Download: 0 times
Share this document with a friend
32
Generics • We have already seen Generics, let’s examine why we use them • In using an ArrayList, we specify <Type> to indicate the type of Object that the ArrayList will store – this is known as a Generic Type, or Generics –<Type> is known as the type parameter – the <> is sometimes called the diamond notation • Generics were first introduced in Java with version 1.5 – They are roughly based on a C++ concept called templates – We do not necessarily need Generics as instead we could always declare a variable to be of type Object, but then we would have to perform casting or downcasting of the object, Generics is cleaner
Transcript
Page 1: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Generics• We have already seen Generics, let’s examine why we

use them• In using an ArrayList, we specify <Type> to indicate

the type of Object that the ArrayList will store– this is known as a Generic Type, or Generics– <Type> is known as the type parameter– the <> is sometimes called the diamond notation

• Generics were first introduced in Java with version 1.5– They are roughly based on a C++ concept called templates– We do not necessarily need Generics as instead we could

always declare a variable to be of type Object, but then we would have to perform casting or downcasting of the object, Generics is cleaner

Page 2: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

ArrayList• We have seen that the ArrayList can be used to store any type of

Object• We can either declare an ArrayList without a specific type (this

would be using the ArrayList pre Java 1.5) or we can declare the ArrayList using Generics – with Generics, it cuts down on the need to perform casting– ArrayList names=new ArrayList( );– String temp=(String) names.get(i);

• Without the cast, we get syntax errors and even with the cast we may get a compiler warning (requiring that we use @Suppresswarnings)

• By creating a Generic type, we avoid having to cast the result– ArrayList<String> s=new ArrayList<String>( );– s.add(“Susan”);– String s2 = s.get(0);

Page 3: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Declaring a Generic Type• We allow the user program to specify the type to be used for

a container class so that, when the container class itself is written, there is no commitment to the type being stored (as long as the type is an object of some kind)– ArrayList<Integer> myints=new ArrayList<Integer>();

– ArrayList<Tank> mytanks=new ArrayList<tank>();

• Generics permit stronger type checking to reduce the number of possible run-time errors that might arise when the user is dealing with Objects

• We can also write our own Generic classes– We will also be examining several Java container classes (in the

next couple of chapters) that were defined using Generics to give us greater flexibility in their usage

Page 4: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

java.util.ArrayList

+ArrayList()

+add(o: Object) : void

+add(index: int, o: Object) : void

+clear(): void

+contains(o: Object): boolean

+get(index: int) : Object

+indexOf(o: Object) : int

+isEmpty(): boolean

+lastIndexOf(o: Object) : int

+remove(o: Object): boolean

+size(): int

+remove(index: int) : boolean

+set(index: int, o: Object) : Object

java.util.ArrayList<E>

+ArrayList()

+add(o: E) : void

+add(index: int, o: E) : void

+clear(): void

+contains(o: Object): boolean

+get(index: int) : E

+indexOf(o: Object) : int

+isEmpty(): boolean

+lastIndexOf(o: Object) : int

+remove(o: Object): boolean

+size(): int

+remove(index: int) : boolean

+set(index: int, o: E) : E

(a) ArrayList before JDK 1.5 (b) ArrayList in JDK 1.5

Comparing ArrayList and ArrayList<E>

Page 5: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Defining a Generic Class• To indicate that a class we are writing is generic, add

<placeholder> to the class header after the class name– The placeholder is usually a single letter, using the following

naming convention• E – used with Java container types like ArrayList• K, V – used for Key and Value (when you have a Key-Value pair)• N – used if the type is to be restricted to a numeric type of Object• T – generic Type

– Example: public class MyGenericClass<T> {…}

• In your class, use T as the type to declare whatever instance datum is necessary– private T foo;

• To create a variable of a Generic class, you must the type in the declaration as in – MyGenericClass<Integer> myInt;

Page 6: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Simple Example: Boxing• Recall that for all primitive types, we have

equivalent Classes that “box” the type so that we can treat a datum of a primitive type as an Object

• Let’s create a generic Box class to box any Object–We might do this so that the programmer who wishes

to utilize different types of objects will not have to use casts

–What should a Boxing class have?• It needs to store the object itself• We need an accessor (get) and mutator (set)• We might also implement a toString

Page 7: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

public class Box<T> {

private T item;

public Box(T item) {this.item=item;

}

public T get() {return item;

}

public void set(T item) {this.item=item;

}

public String toString() {if(item!=null) return ""+item;else return "not set";

}}

Our box class is very basic

The type of Object is recorded as T (filled in when you declare a variable of type Box)

T is used to specify the type for item when declared as an instance datum, or passed asa parameter to a method or returned from a method

Note that T needs to have atoString implemented or this returns the address of item

Page 8: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

public class BoxUsers { public static void main(

String[] args) { Box<String> a; Box<Integer> b; Box<Double> c; Box<Object> d=null; a=new Box<>("hi there"); b=new Box<>(100); c=new Box<>(100.1); System.out.println(a.get()); a.set("bye bye"); c.set(c.get()+1); System.out.println(a); System.out.println(c); System.out.println(d); }}

Output:

hi therebye bye101.1null

What if we want to doc.set(b.get( )+1);

This yields an error because b.get( ) returns an Integer and c.set expects a Double, so instead use c.set(new Double(b.get( )+1));

Page 9: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

import java.util.*;public class GenericStack<E> { private ArrayList<E> stack; public GenericStack(){

stack=new ArrayList<E>(); } public int getSize() {

return stack.size(); } public E peek() {

return stack.get(stack.size()-1); } public E pop() {

E returnItem=stack.get(stack.size()-1);stack.remove(stack.size()-1);return returnItem;

} public void push(E newItem) {

stack.add(newItem); } public boolean isEmpty() {

return stack.isEmpty(); }}

A Generic Stack Class

GenericStack<String> stack= new GenericStack<>();stack.push(“string1”);stack.push(“string2”);…

Page 10: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Bounded Generic Classes• We can restrict the types acceptable in a Generic class

by “bounding” the generic class using – public class Name<placeholder extends Type>

• Where Type is the type we want to restrict our Generic classes to, such as Number

• We might want a Box-like generic which is limited to Numeric Objects – we would employ <T extends Number>public class NumericBox<T extends Number> { T item; public NumericBox(T item){this.item=item;} public boolean bigger(T item2){

if(item.doubleValue()>item2.doubleValue()) return true; else return false;

}}

Page 11: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Example: Unique Pair Class• Let’s expand on the Box example by implementing a

Generic class which stores 2 items of the same type of Object

• In this case, we want to make sure that the two items (instance data) are unique– Arbitrarily, we will decide that if the two items are equal (as

determined by Comparable), the second of the two will be set to null

• To test for uniqueness, we need to have Comparable implemented on the class of the Objects

• How can we ensure that the class being utilized implements Comparable?– We need to state that the type, T, implements Comparable by

specifying <T extends Comparable<T>>

Page 12: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

public class UniquePair<T extends Comparable<T>> { private T item1, item2; public UniquePair(T i1, T i2){

item1=i1;item2=i2;if(item1.compareTo(item2)==0) item2=null;

} public T get1() { return item1; } public T get2() { return item2; } public void set1(T i1) {

item1=i1;if(item1.compareTo(item2)==0) item2=null;

} public void set2(T i2){

item2=i2;if(item1.compareTo(item2)==0) item2=null;

}}

UniquePair<Integer> a=new UniquePair<>(new Integer(10),new Integer(20));

a.set1(new Integer(20));

results in object a storing (20, null)

Page 13: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Generic Classes with More than 1 Type• UniquePair had 2 instance data of the same type– What about a Generic class of multiple types?

• The common example is a Key-Value pair, sometimes called a tuple– The idea is that we are storing an attribute’s name and the

value of that attribute• a person can be described using multiple key-value pairs such as

“Name: Frank Zappa”, “Occupation: Musician”, “Height: 70 inches”, etc

• Let’s implement a Key-Value pair which has methods to determine if another Key-Value pair has the same Key and Value – Through Comparable, we compare both Keys and both

Values

Page 14: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

public class KeyValuePair<K extends Comparable<K>,V extends Comparable<V>> {

private K key; private V value; public KeyValuePair(K k2, V v2)

{key=k2;value=v2;} public K getKey(){return key;} public V getValue(){return value;} public void setKey(K k2){key=k2;} public void setValue(V v2) {value=v2;}

@SuppressWarnings("unchecked") public boolean equal(KeyValuePair kvp){

if(key.compareTo((K)kvp.getKey())==0&&value.compareTo((V)kvp.getValue())==0)

return true; else return false;

}}

NOTE: The Java compilerdoes not like the compareTo operations and so we have to suppress a warning to ensureproper compilation

Page 15: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

@SuppressWarnings("unchecked")public static void main(String[] args){ KeyValuePair<String,Integer> k1=new

KeyValuePair("Height", 71); KeyValuePair<String,Integer> k2=new

KeyValuePair("Height", 69); System.out.println(k1.equal(k2));}

Here, the Java compilerdoes not like the equaloperation and so wehave to suppress a warning to ensureproper compilation

public class KeyValuePair<K extends Comparable<K>,V extends Comparable<V>>

Let’s take a closer look at the class’ header

Aside from <K, V> we say that both extendComparable and add Comparable<K> andComparable<V> so that compareTo expects a KeyValuePair<K,V>

Page 16: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Raw Types• In some cases, you can omit the <Type> when declaring a

variable of a Generic type – this is known as a raw type– ClassName variable=new Classname();

• This is available for Generic classes which were originally defined in Java prior to the inclusion of Generics in Java (pre Java 1.5), such as ArrayList or classes that implement a Generic interface like Comparable– This allows us to maintain backward compatibility

• Raw types are unsafe and result in unchecked warnings generated by the Java compiler resulting in the program not compiling– You can use a different compiler setting to avoid unchecked warnings– We can also avoid the unchecked warnings by suppressing them by

placing the following compiler directive before any method that might cause this warning

– @Suppresswarnings(“unchecked”)

Page 17: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Implementing Comparable• Interfaces themselves can be generic when written• If we want our Generic class to implement an interface,

then we have to make sure we implement the proper method(s) for the interface

• Let’s implement Comparable for our Box class– If we have two Box objects, compare them using compareTo– The problem is that we can’t just implement our own

compareTo method that somehow tests the internal portions of our Object because we may not have access to those Objects’ instance data

– But as we saw, we can ensure that the Objects are Comparable by using <T extends Comparable<T>>

– Since T is Comparable, we can then implement our own compareTo method which calls T’s compareTo method

Page 18: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Implementing a Comparable Box• The class header is now more complicated as not only

does T extend Comparable, but this class implements Comparable<T>– public class ComparableBox<T extends Comparable<T>> implements Comparable<T>

• We have to implement compareTo– recall compareTo expects to receive an Object but this will

be utilized in a setting like this:• ComparableBox<T> foo.compareTo(ComparableBox<T> bar)

– we have to “strip out” the T object from bar before we can compare it to foo’s T object and so we wind up writing two compareTo methods

Page 19: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

public class ComparableBox<T extends Comparable<T>> implements Comparable<T> {

T item;// ... as before

public int compareTo(ComparableBox<T> item2){

return compareTo(item2.get());}

@Overridepublic int compareTo(T item2){

return item.compareTo(item2);}

}

Invoked by code like this:

ComparableBox<String> a=new ComparableBox<>("hi there");ComparableBox<String> b=new ComparableBox<>("bye bye");System.out.println(a.compareTo(b));

Page 20: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

A Generic Matrix Class• The book covers a Generic Matrix Class– A Matrix is a 2-D array– The type of value to be stored will be Generic– We often perform arithmetic operations on matrices so we will

restrict the Generic type to extend Number

• The author implements GenericMatrix as an abstract class requiring that subclasses implement the abstract methods– Why? Some operations will have to be implemented differently

based on type– So for instance, an Integer version might implement an add

method one way while a RationalNumber version would use a different approach

– But at the same time, other operations like copyMatrix and printMatrix can be implemented strictly with the Generic type• see details on pages 785-789

Page 21: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Generic Methods• We can write static methods which receive a Generic type

of parameter rather than a specifically typed parameter– We could also receive an Object, but that may require casting

and if we need to use an object of the same type in the method, we wouldn’t know which type to use or to cast

• Structure of a Generic method header:– public static <type> returntype name(params){…}

• To invoke a Generic method, much like creating a variable of a Generic class, you need to specify the type in the method call as in– SomeClass.<type>methodName(params);

• As with Generic classes, you can bound the type using <Type extends Class> and use multiple types as in <String,Integer>

Page 22: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

public static <E extends Comparable<E>> void sort(E[] list){ E min; int minIndex; for(int i=0;i<list.length-1;i++) { min=list[i]; minIndex=i; for(int j=i+1;j<list.length;j++) { if(min.compareTo(list[i]>0) { min=list[j] minIndex=j; } } list[minIndex]=list[i]; list[i]=min; }}

Generic Selection SortThis selection sort can be called with any array of some Comparable Objects whether that is String, Character, Integer, Double, etc

Assume this is placed in a class calledGenericSortClass and we have anObject of this type called foo

Call the function withfoo.<Integer>sort(intArray);Or from within GenericSortClass assort(intArray);

Page 23: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Wildcard Types• Recall the NumericBox bounded our Box’s Object to

be a subclass of Number, but we don’t have to resort to bounding the class itself

• Instead, we can place a bound on the parameter type allowed for a generic method– Let’s see how to create a “greaterThan” method for two

Boxes where we expect the Box types to be Numeric (e.g., Box<Number>)

–We employ another Generic mechanism called a wildcard• wildcards are only allowed in static methods

– As a static Generic method will only operate on parameters and not non-static instance data, we will pass both objects to be operated on to this method

Page 24: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Enhancing Box with a Wildcard• First, we go back to our Box class without having <T

extends Number>• So this is just an ordinary Box class in which any Object

type can be used to declare an object such as– Box<String> b1;– Box<Integer> b2;– Box<Student> b3; // assumes we have a Student class

• But if we want to implement a greaterThan static method, we need to make sure that the type (String, Integer, Student) is Comparable – in our case we will restrict it even further to be a Number type

• So our greaterThan static method will bound the type and not the Box class itself

Page 25: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Bounding with a Wildcard• The notation for our static method is similar to our

previous example of a static method (although in this case, we are receiving 2 parameters, not one)– Instead of the two Box parameters being <T>, we use <?

extends Number> to indicate any class/subclass of Numberpublic static <T> boolean greater(Box<? extends Number> item1,

Box<? extends Number> item2) { if(item1.get().doubleValue()>item2.get().doubleValue())

return true; else return false;}

Box<String> a=new Box<>("Hi there");Box<Integer> b=new Box<>(12);Box<Double> c=new Box<>(11.5);System.out.println(Box.greater(a,c));System.out.println(Box.greater(b,c));

Causes a syntax error becauseobject a (Box<String>) is not a subtype of Number

we use double since it is the widest numeric type(any numeric type can be converted to a double)

Page 26: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Another Example• Returning to our previously written

GenericStack, we create a multiply method– We can only multiply Numbers– We will multiply pairwise values of two

GenericStacks, adding each product together and return the result as a double since double is the widest type of Number

public static <E> double multiply(GenericStack<? extends Number> s1, GenericStack<? extends Number> s2) {

double temp=0; int smaller=s1.getSize(); if(s2.getSize()<smaller) smaller=s2.getSize(); for(int i=0;i<smaller;i++)

temp=s1.pop().doubleValue()*s2.pop().doubleValue(); return temp;}

Page 27: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Continued• Now let’s write a method to multiply all pairs of the Stack,

creating a new Stack (e.g., top element of the new Stack is the top of s1 * top of s2, etc, the method will return a Stack

• Our method header would look like this:• public static <E> GenericStack<sometype>

multiplyStacks (GenericStack<? extends Number> s1, GenericStack<? extends Number> s2)• But what type of GenericStack should the method return?

What Java does not allow is a return type of ObjectType<?> - that is, we cannot use the wildcard character in the return type anywhere, we must specify an exact type

Page 28: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Continued• For our return type, let’s re-examine the previous multiply

method• We used double as the return type, why? Because it is the

widest numeric type (aside from BigInteger and BigDecimal)– No matter what numeric type is used for s1 and s2, we can coerce

them into doubles

• So we will use GenericStack<Double> as our return type

public static <E> GenericStack<Double> multiplyStacks(GenericStack<? extends Number> s1,

GenericStack<? extends Number> s2) { GenericStack<Double> temp=new GenericStack<>(); int smaller=s1.getSize(); if(s2.getSize()<smaller) smaller=s2.getSize(); for(int i=0;i<smaller;i++)

temp.push(s1.pop().doubleValue()*s2.pop().doubleValue()); return temp;}

Page 29: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

More on Wildcards• If you want to reference any type of object, use <?>

– known as an unbounded wildcard

• If you want to reference any type of object at or lower than a given class, use <? extends class>– known as an upper bound– note that <?> is equal to <? extends Object>

• If you want to reference any type of object at or higher than a given class, use <? super class>– known as a lower bound– you would use a lower bound if there were specific subclasses

that you wanted to prohibit from being usable as parameters

• Note that while parameters of a generic method can use wildcards, if the method is to return a generic type, it must be specified (e.g., as T) and not use a wildcard

Page 30: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Type Erasure• Generic types are filled in by the compiler as the

compiler converts your Java code• Thus, at run time, all Generic types are actually converted

into raw types– This is because you must specify the type to be used in a

Generic class when you declare your variable• Box<Integer> b1 = … causes a Box type to be implemented

with Integer as the type (for all occurrences in Box of T)• Box<String> b2 = … causes a Box type to be implemented with

String

– Once compilation is over, the “generic” aspect of the class is erased, now the object contains a specific type

– Thus, you cannot apply a generic at run time, only at compile time

– This idea is known as type erasure

Page 31: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Restrictions on Generics• You cannot create an instance of a variable using a

Generic type using new as in– E foo=new E( );– You will only be able to assign foo to a parameter of type E

as in • public MyClass(E bar) {E foo=bar;}

• You cannot create an array of a Generic type as in– E[ ] foo=new E[100];– There are ways to get around this, for instance creating an

ArrayList of type E as in• ArrayList<E> foo=new ArrayList<>();

– or by creating an array of objects and casting as in • E[] foo=(E[])new Object[100];

– although this solution causes an unchecked warning

Page 32: Generics We have already seen Generics, let’s examine why we use them In using an ArrayList, we specify to indicate the type of Object that the ArrayList.

Restrictions Continued• You cannot create a Generic using a primitive type as

in Box<int> b1=…• When using a generic method, since it has to be static,

it cannot access non-static members of your class– Instead, think of the method as only operating on

parameters

• Similarly, you cannot declare a static variable to be of a Generic type

• You cannot use casts or instanceof on a parameterized type

• You cannot create parameterized Exceptions– you can use a parameterized type in a throws clause

• You cannot overload methods with parameterized types


Recommended