In using an ArrayList we specify lt Type gt to indicate the type of Object that the ArrayList will store this is known as a Generic Type or Generics lt Type gt is known as the ID: 933498
Download Presentation The PPT/PDF document "Generics We have already seen Generics, ..." is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.
Slide1
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
Slide2ArrayList
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);
Slide3Declaring 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
Slide4Comparing
ArrayList
and
ArrayList
<E>
Slide5Defining 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
;
Slide6Simple 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
Slide7public 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 basicThe 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 methodNote that T needs to have atoString implemented or this returns the address of item
Slide8public 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.1nullWhat if we want to do
c.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));
Slide9import
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”);…
Slide10Bounded 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; }}
Slide11Example: 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>>
Slide12public 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)
Slide13Generic 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
Slide14public 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 compiler
d
oes not like the compareTo operations and so we have to suppress a warning to ensureproper compilation
Slide15@
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 weh
ave to suppress a warning to ensureproper compilationpublic class KeyValuePair<K extends Comparable<K>,V extends Comparable<V>>Let’s take a closer look at the class’ headerAside from <K, V> we say that both extend
Comparable and add Comparable<K> and
Comparable<V> so that
compareTo
expects a KeyValuePair<K,V>
Slide16Raw 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”)
Slide17Implementing 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
Slide18Implementing 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
Slide19public class
ComparableBox
<T extends Comparable<T>> implements Comparable<T
> {
T item;
// ... as before
public int
compareTo
(
ComparableBox
<T> item2)
{
return compareTo(item2.get()); } @Override public 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));
Slide20A 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 typesee details on pages 785-789
Slide21Generic 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>
Slide22public 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, etcAssume this is placed in a class calledGenericSortClass and we have anObject of this type called fooCall the function with
foo.<Integer>sort(
intArray
);
Or from within GenericSortClass assort(intArray);
Slide23Wildcard 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
Slide24Enhancing 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
Slide25Bounding 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 Number
public
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 Numberwe use double since it is the widest numeric type(any numeric type can be converted to a double)
Slide26Another 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;}
Slide27Continued
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
Slide28Continued
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 doublesSo 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;}
Slide29More 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 boundnote 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 parametersNote 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
Slide30Type 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
Slide31Restrictions 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
Slide32Restrictions 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