text: Bounded Types
  In some situations you define a generic type where you want to constrain the type arguments that are supplied to define a class instance so that they extend a particular class, or implement specific interfaces, or even both. The reason for this is that your generic type has to make some assumptions about the capabilities of the objects an instance of the type is dealing with. Such constraints are called type parameter bounds. The first bound that you specify for a type parameter can be either a class type or an interface type. Any additional bounds after the first for a type parameter can be interface types only.
  For example, assume that you want to create a generic class that contains a method that call the method brandAndModel( ) of an Mobile class. Furthermore, you want to use the class to call the brandAndModel( ) of any type of Mobile(Mobile subclasses). Thus, you want to specify the type of the Mobile generically, using a type parameter. To create such a class, you might try something like this :
  In Gen class, the show( ) method attempts to call the brandAndModel( ) method in the specified type parameter. Because all mobile classes are subclasses of Mobile, and Mobile defines the method brandAndModel( ). The trouble is that the compiler has no way to know that you are intending to create Gen objects using only Mobile types. Thus, when you try to compile Gen, an error is reported that indicates that the brandAndModel( ) method is unknown. To solve this problem, you need some way to tell the compiler that you intend to pass only Mobile types to T. Furthermore, you need some way to ensure that only Mobile types are actually passed.
  To handle such situations, Java provides bounded types. When specifying a type parameter, you can create an upper bound that declares the superclass from which all type arguments must be derived. This is accomplished through the use of an extends clause when specifying the type parameter, as shown here :
  <T extends Mobile> specifies that T can only be replaced by Mobile, or subclasses of Mobile. Thus, superclass(Mobile) defines an inclusive, upper limit. T is now bounded by Mobile, the Java compiler knows that all objects of type T can call brandAndModel( ) because it is a method declared by Mobile. This is, by itself, a major advantage. However, as an added bonus, the bounding of T also prevents nonmobile Gen objects from being created. For example, the following lines of code is error and will not compile :
  Because Integer and String are not a subclass of Mobile.
  Program
  Another Example
  In the following program, in Gen, the total( ) method attempts to obtain the double version of each number in the tarray array by calling doubleValue( ). Because the type T is now bounded by Number, the Java compiler knows that all objects of type T can call doubleValue( ) because it is a method declared by Number. The Number is a superclass of all numeric classes, such as Integer, Float and Double, and Number defines the doubleValue( ) method, this method is available to all numeric wrapper classes. This is, by itself, a major advantage. However, as an added bonus, the bounding of T also prevents nonnumeric Gen objects from being created. For example, the following line of code is error and will not compile :
  Because String is not a subclass of Number.
  Program
  class Mobile { private String brand; private String model; Mobile(String br, String mo) { brand = br; model = mo; } void brandAndModel() { System.out.println(brand + " -> " + model); } } class GalaxyS9 extends Mobile { GalaxyS9() { super("Samsung", "GalaxyS9"); } } class GalaxyNote9 extends Mobile { GalaxyNote9() { super("Samsung", "GalaxyNote9"); } } class Gen<T extends Mobile> { T t; Gen(T tt) { t = tt; } void show() { t.brandAndModel(); } } public class Javaapp { public static void main(String[] args) { Gen<GalaxyS9> mgen1 = new Gen<GalaxyS9>(new GalaxyS9()); mgen1.show(); Gen<GalaxyNote9> mgen2 = new Gen<GalaxyNote9>(new GalaxyNote9()); mgen2.show(); Gen<Mobile> mgen3 = new Gen<Mobile>(new Mobile("Samsung", "A7")); mgen3.show(); mgen3 = new Gen<Mobile>(new GalaxyS9()); mgen3.show(); mgen3 = new Gen<Mobile>(new GalaxyNote9()); mgen3.show(); } }
  Program Source
  class Gen<T extends Number> { T tarray[]; Gen(T tar[]) { tarray = tar; } double total() { double total = 0; for (int i = 0; i < tarray.length; i++) { total += tarray[i].doubleValue(); } return total; } } public class Javaapp { public static void main(String[] args) { Gen<Integer> gen1 = new Gen<Integer>(new Integer[]{10, 20, 30, 40, 50}); System.out.println("gen1 total : " + gen1.total()); Gen<Double> gen2 = new Gen<Double>(new Double[]{1.1, 2.2, 3.3, 4.4, 5.5}); System.out.println("gen2 total : " + gen2.total()); Gen<Float> gen3 = new Gen<Float>(new Float[]{5.2f, 6.4f, 7.8f, 8.6f, 9.2f}); System.out.println("gen3 total : " + gen3.total()); Gen<Number> gen4 = new Gen<Number>(new Number[]{55, 6.4f, 45.67, 45, 9.2f}); System.out.println("gen4 total : " + gen4.total()); } }