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