1 /* GenericVectors.java 2 * Created on February 21, 2006, 4:53 PM 3 */ 4 package Generics; 5 6 import java.util.Vector; 7 8 /** 9 * Important note: Generic types are checked at COMPILE TIME not RUN TIME! This 10 * is to enforce the strong/static typing inherent in Java. 11 * 12 * This set of examples uses the hierarchy of "Food" classes in {@link Food}. 13 * I don't show how to create generified classes in this example, but instead show 14 * how to properly use existing generified classes and how to create generified 15 * methods which accept and return generified types. 16 * 17 * @author Adam J. Conover 18 */ 19 public class GenericVectors { 20 21 /** 22 * Main method. Serves as the entry point into our test application. 23 * @param args 24 */ 25 public static void main(String[] args) { 26 27 // Declare some objects of differnet types. Note: The ONLY reason the 28 // types are declared as they are here is for the sake of example! 29 Fuji f1 = new Fuji(335); // The variable is an exact type. 30 Apple f2 = new Gala(300); // The variable is a super type. 31 Orange f3 = new Orange(250); // The variable is an exact type. 32 Orange f4 = new Sunkist(); // The variable is a super type. 33 Apple f5 = new HoneyCrisp(350); // The variable is a super type. 34 Fruit f6 = new Sunkist(); // The variable is an even more generic super type. 35 36 37 // A Vector that will hold only apples 38 Vector<Apple> apppleBasket = new Vector<Apple>(); 39 apppleBasket.add(f1); // A Fuji "is an" Apple, so it's ok. 40 apppleBasket.add(f2); 41 42 // A Vector that will hold only Oranges 43 Vector<Orange> orangeBasket = new Vector<Orange>(); 44 orangeBasket.add(f3); 45 orangeBasket.add(f4); 46 47 // A Vector that will hold any type of Fruit 48 Vector<Fruit> fruitBasket = new Vector<Fruit>(); 49 50 // Since all of the above are of type "Fruit" they can all go in the basket. 51 fruitBasket.addAll(apppleBasket); // add the Vector of Apples since Apples are Fruit 52 fruitBasket.addAll(orangeBasket); // add the Vector of Oranges since Oranges are Fruit 53 fruitBasket.add(f5); 54 fruitBasket.add(f6); 55 56 // Total the amount of calories in the entire basket. 57 float caloriesInBasket = 0; 58 for (Fruit f : fruitBasket) { 59 caloriesInBasket += f.getTotalCalories(); 60 } 61 System.out.println("Calories in basket: " + caloriesInBasket); 62 63 64 // Create a new basket (from the old one) that is even more generic. 65 Vector<Food> foodBasket = new Vector<Food>(fruitBasket); 66 67 68 // Since we can put fruit in a food basket, this call is perfectly legal. 69 putFirst(foodBasket, new Fuji(327)); 70 71 // Since we know that the basket has Food in it we can do this: 72 Food foodItem = getFirst(foodBasket); 73 74 75 // Apples and Oranges have "Fruit" in common. Since Fruit implements 76 // Comparable, this call will work with or without wildcard caputure. 77 // Try replacing the "? super T" portion of getBiggest() with just "T" 78 // and see what happens. This will still compile. The next line will not. 79 Fruit a = getBiggest(f5, f3); 80 81 // And who said you couldn't compare apples and oranges? 82 83 // However, "Wildcard capture" is nexessary to make this work, since 84 // Apple is a subtype of something that is comparable to Fruit. Both 85 // "Fuji" and "Gala" are of type "Apple". 86 Apple b = getBiggest(new Gala(302), new Fuji(367)); 87 88 89 // This will not work (Vector<Fruit> does not extend Vector<Food>) 90 // Vector<Food> headAndTail = getHeadAndTail(basket); 91 92 // However this does work: 93 Vector<? extends Food> headAndTail1 = getHeadAndTail(fruitBasket); 94 95 // Normally, you would probably just want to do this: 96 Vector<Fruit> headAndTail2 = getHeadAndTail(fruitBasket); 97 } 98 99 //////////////////////////////////////////////////////////////////////////// 100 // EXAMPLE METHODS BELOW THIS POINT 101 //////////////////////////////////////////////////////////////////////////// 102 /** 103 * "? extends Food" states: "I don't know what type of vector I'll be dealing 104 * with, but I know I can legally take Food from it." 105 * 106 * @param basket The Vector of food items. 107 * @return the first item in the vector. 108 */ 109 static Food getFirst(Vector<? extends Food> basket) { 110 System.out.println("IN: getFirst(Vector<? extends Food> basket))"); 111 return basket.firstElement(); 112 113 // Can't to this: 114 // basket.add(new Fuji(333)); 115 116 /* The above wont work since we don't know the *actual* type of object 117 * bound to the originating Vector; whe just know it has Food as a super-type. 118 * For example, a Vector of Poultry items could have been passed to this 119 * method. Though we can treat Apples as Food, we can't assume that -- 120 * just because Apple extends Food -- we could put the Apple into a an 121 * arbirary Food Vector. I.e. It could be a narrowly restricted Vector 122 * of only one specific type of Food item. 123 * 124 * Note: Though it could be argued that once we take one Apple out of the 125 * Vector, we know we can put other Apples into it. However, since this 126 * method may be called any number of valid Vectors, there is no wat to know 127 * that until "run-time". The static typing enforced by the compiled must 128 * be checkable at "compile-time". 129 */ 130 } 131 132 /** 133 * "? super Food" states: "I don't know what type of vector I'll be dealing 134 * with, but I know I can legally put Food into it." 135 * 136 * @param basket A basket (vector) that food items can be put into. 137 * @param f An instance of an apple (which extends food). 138 */ 139 static void putFirst(Vector<? super Fruit> basket, Apple f) { 140 System.out.println("IN: putFirst(Vector<? super Fruit> basket, Apple f)"); 141 basket.add(0, f); 142 143 // Can't do this: 144 // Fruit f = basket.firstElement(); 145 146 /* The above wont work since we don't actually know what *other* element 147 * types might be in the originating Vector. For example, a Vector of Food 148 * items could have been passed to this method. Though we know we can put 149 * Fruit into a Food basket, the first item in the Vector might already be 150 * of type Poultry! In other words, there is no way of knowing what other 151 * data-types the Vector might already contain. 152 * 153 * The best you could do would be... 154 * 155 * Object obj = basket.firstElement(); 156 * 157 * ... Since everthing is alt guarenteed to be an object. 158 */ 159 } 160 161 /** 162 * An even more generic version of the getFirst() method. "Type inference" is 163 * used by the compiler to make sure that what is returned is of the same type 164 * as what is actually contained in the Vector. 165 * 166 * @param basket A basket (vector) that food items can be put into. 167 * @return the first item in the vector, which will be known to be of type T 168 */ 169 static <T> T getFirst(Vector<? extends T> basket) { 170 System.out.println("IN: getFirst(Vector<? extends T> basket)"); 171 return basket.firstElement(); 172 } 173 174 /** 175 * An even more generic version of the putFirst() method. Type inference is 176 * used by the compiler to make sure that the second parameter is actually 177 * something that can be placed into the vector in the first parameter. 178 * 179 * @param basket A basket (vector) that items of type T can be put into. 180 * @param f An instance of type T which can be put into the basket (vector). 181 */ 182 static <T> void putFirst(Vector<? super T> basket, T f) { 183 System.out.println("IN: putFirst(Vector<? super T> basket, T f)"); 184 basket.add(0, f); 185 } 186 187 /** 188 * This is a more complicated example. It says, "Take two things which are 189 * comparable, and specifically comparable to each other, but one may be a 190 * super-type of the other (or share a super type). 191 * 192 * @param <T> The generic type of object being compared. 193 * @param obj1 The first object in the comparison. 194 * @param obj2 The second object in the comparison. 195 * @return The largest object, as a result of the comparison. 196 */ 197 static <T extends Comparable<? super T>> T getBiggest(T obj1, T obj2) { 198 if (obj1.compareTo(obj2) >= 0) { 199 return obj1; 200 } else { 201 return obj2; 202 } 203 } 204 205 /** 206 * Though things can get a bit more complicated in theory, this method is about 207 * as complex as things actually get in practice. This methods will get the head 208 * and tail items of a vector of items which are comparable to each other and 209 * return a new Vector with the two items in ascending order. 210 * 211 * @param <T> The generic type of object contained in the Vector. 212 * @param vect The vector of generic types. 213 * @return The head and tail items of the original vector in a new vector, 214 * in ascending order. 215 */ 216 static <T extends Comparable<? super T>> Vector<T> getHeadAndTail(Vector<T> vect) { 217 T headItem = vect.firstElement(); 218 T tailItem = vect.lastElement(); 219 220 Vector<T> rtnVector = new Vector<T>(); 221 222 if (headItem.compareTo(tailItem) <= 0) { 223 rtnVector.add(headItem); 224 rtnVector.add(tailItem); 225 } else { 226 rtnVector.add(tailItem); 227 rtnVector.add(headItem); 228 } 229 230 return rtnVector; 231 } 232 } 233 234 235