Presentation is loading. Please wait.

Presentation is loading. Please wait.

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.

Similar presentations


Presentation on theme: "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."— Presentation transcript:

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 will store – this is known as a Generic Type, or Generics – 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

2 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 s=new ArrayList ( ); –s.add(“Susan”); –String s2 = s.get(0);

3 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 myints=new ArrayList (); –ArrayList mytanks=new ArrayList (); 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

4 Comparing ArrayList and ArrayList

5 Defining a Generic Class To indicate that a class we are writing is generic, add 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 {…} 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 myInt;

6 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

7 public class Box { 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 as a parameter to a method or returned from a method Note that T needs to have a toString implemented or this returns the address of item

8 public class BoxUsers { public static void main( String[] args) { Box a; Box b; Box c; Box 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 there bye 101.1 null What 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));

9 import java.util.*; public class GenericStack { private ArrayList stack; public GenericStack(){ stack=new ArrayList (); } 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 stack= new GenericStack<>(); stack.push(“string1”); stack.push(“string2”); …

10 Bounded Generic Classes We can restrict the types acceptable in a Generic class by “bounding” the generic class using – public class Name 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 public class NumericBox { T item; public NumericBox(T item){this.item=item;} public boolean bigger(T item2){ if(item.doubleValue()>item2.doubleValue()) return true; else return false; }

11 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 >

12 public class UniquePair > { 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 a=new UniquePair<>(new Integer(10), new Integer(20)); a.set1(new Integer(20)); results in object a storing (20, null)

13 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

14 public class KeyValuePair,V extends Comparable > { 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 does not like the compareTo operations and so we have to suppress a warning to ensure proper compilation

15 @SuppressWarnings("unchecked") public static void main(String[] args){ KeyValuePair k1=new KeyValuePair("Height", 71); KeyValuePair k2=new KeyValuePair("Height", 69); System.out.println(k1.equal(k2)); } Here, the Java compiler does not like the equal operation and so we have to suppress a warning to ensure proper compilation public class KeyValuePair<K extends Comparable,V extends Comparable > Let’s take a closer look at the class’ header Aside from we say that both extend Comparable and add Comparable and Comparable so that compareTo expects a KeyValuePair

16 Raw Types In some cases, you can omit the 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”)

17 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 > – Since T is Comparable, we can then implement our own compareTo method which calls T’s compareTo method

18 Implementing a Comparable Box The class header is now more complicated as not only does T extend Comparable, but this class implements Comparable –public class ComparableBox > implements Comparable We have to implement compareTo – recall compareTo expects to receive an Object but this will be utilized in a setting like this: ComparableBox foo.compareTo(ComparableBox 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

19 public class ComparableBox > implements Comparable { T item; //... as before public int compareTo(ComparableBox item2) { return compareTo(item2.get()); } @Override public int compareTo(T item2) { return item.compareTo(item2); } Invoked by code like this: ComparableBox a=new ComparableBox<>("hi there"); ComparableBox b=new ComparableBox<>("bye bye"); System.out.println(a.compareTo(b));

20 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

21 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 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. methodName(params); As with Generic classes, you can bound the type using and use multiple types as in

22 public static > 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 Sort This 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 called GenericSortClass and we have an Object of this type called foo Call the function with foo. sort(intArray); Or from within GenericSortClass as sort(intArray);

23 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 ) – 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

24 Enhancing Box with a Wildcard First, we go back to our Box class without having So this is just an ordinary Box class in which any Object type can be used to declare an object such as – Box b1; – Box b2; – Box 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

25 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, we use to indicate any class/subclass of Number public static boolean greater(Box item1, Box item2) { if(item1.get().doubleValue()>item2.get().doubleValue()) return true; else return false; } Box a=new Box<>("Hi there"); Box b=new Box<>(12); Box c=new Box<>(11.5); System.out.println(Box.greater(a,c)); System.out.println(Box.greater(b,c)); Causes a syntax error because object a (Box ) 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)

26 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 double multiply(GenericStack s1, GenericStack 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; }

27 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 GenericStack multiplyStacks (GenericStack s1, GenericStack 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

28 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 as our return type public static GenericStack multiplyStacks (GenericStack s1, GenericStack s2) { GenericStack 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; }

29 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 – known as an upper bound – note that is equal to If you want to reference any type of object at or higher than a given class, use – 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

30 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 b1 = … causes a Box type to be implemented with Integer as the type (for all occurrences in Box of T) Box 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

31 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 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

32 Restrictions Continued You cannot create a Generic using a primitive type as in Box 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


Download ppt "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."

Similar presentations


Ads by Google