Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
CITS2210 Object-Oriented Programming Topic 9 Java: Generics Summary: Generics allow code to be written that is parameterized with respect to one or more types. This allows a class or method to be used uniformly for all object types, or where appropriate for all subtypes of a particular type. 1 The Motivation for Generics Suppose we want to write a class ArrayPlus that works like an array, but has extra methods. – We may want methods such as “delete an element and shuffle the rest”. public class ArrayPlus { private String[] array; private int num = 0; } public ArrayPlus(int maxSize) { array = new String[maxSize]; } public void add(String e) { array[num++]=e; } public String get(int pos) { return array[pos]; } public void delete(int pos) { for(int i=pos; i<num-1; i++) array[i] = array[i+1]; num--; } – But this class only works for strings! – We don't want to copy the code for each type for which we need arrays. – What can we do? 2 A Second Attempt: Using the type “Object” If we want only one version, then why not use Object as the type? public class ArrayPlus { private Object[] array; private int num = 0; } public ArrayPlus(int maxSize) { array = new Object[maxSize]; } public void add(Object e) { array[num++]=e; } public Object get(int pos) { return array[pos]; } public void delete(int pos) { for(int i=pos; i<num-1; i++) array[i] = array[i+1]; num--; } This will allow us to store any object, which is what we wanted to do. – Primitive types like int and float aren't objects, but can be “boxed” into object types like Integer and Float automatically. See: http://java.sun.com/j2se/1.5.0/docs/guide/language/autoboxing.html 3 Issues with the second attempt But, suppose we use this class to store strings. – When we retrieve using get, the return type is always Object. – The code retrieving will expect a string. – So we'll have to cast to use the result as a string. ((String)myArrayPlus.get(3)).substr(1,3) But, casts are ugly and error prone. – If we accidentally cast to wrong type we'll get a runtime error. – If we accidentally add something that isn't a string, ditto. – – When a variable has type ArrayPlus it's not obvious what kind of objects it's supposed to hold. If we consider array types like String[], they don't have these problems. 4 The Solution: Generics These issues motivated the introduction of generics into Java in version 1.5 (now usually just called version 5). – Prior to this the previous solution was widely used, and found unsatisfactory in practice. Generics are a way of parameterizing interfaces, classes and methods with respect to types. – – This means that interfaces, classes and methods can have type parameters. E.g., this allows the type of elements in an ArrayPlus to be specified. public class ArrayPlus<E> { private ELEM[] array; private int num = 0; } public ArrayPlus(int maxSize) { array = (E[])new Object[maxSize]; } public void add(E e) { array[num++]=e; } public E get(int pos) { return array[pos]; } public void delete(int pos) { for(int i=pos; i<num-1; i++) array[i] = array[i+1]; num--; } 5 Generic parameters The type parameters in generics are like parameters in methods. – – The types aren't specified in the generic class/interface/method definitions. Each time these definitions are used, a particular type is supplied. Conventions type parameters are usually single capital letter variables, with the following in standard usage. E - Element (used extensively by the Java Collections Framework) • K - Key • N - Number • T - Type • V - Value S,U,V etc. - 2nd, 3rd, 4th types • 6 Generics examples – Methods may also have type parameters. – “Bounds” can also be given for type parameters public class Box<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public <U extends Number> void inspect(U u){ System.out.println("T: " + t.getClass().getName()); System.out.println("U: " + u.getClass().getName()); } } public static void main(String[] args) { Box<Integer> integerBox = new Box<Integer>(); integerBox.add(new Integer(10)); integerBox.inspect(20); } To specify additional interfaces that must be implemented, use the & character, as in: <U extends Number & MyInterface> 7 Subtyping for generics Are Box<Integer> and Box<Double> subtypes of Box<Numeric>? It turns out no! Understanding why becomes much easier if you think of tangible objects — things you can actually picture — such as a cage: // A cage is a collection of things, with bars to keep // them in. interface Cage<E> extends Collection<E>; A lion is a kind of animal, so Lion would be a subtype of Animal: interface Lion extends Animal {} Lion king = ...; Where we need some animal, we're free to provide a lion: Animal a = king; A lion can of course be put into a lion cage: Cage<Lion> lionCage = ...; lionCage.add(king); and a butterfly into a butterfly cage: interface Butterfly extends Animal {} Butterfly monarch = ...; Cage<Butterfly> butterflyCage = ...; butterflyCage.add(monarch); 8 Subtyping for generics But what about an "animal cage"? English is ambiguous, so to be precise let's assume we're talking about an "all-animal cage": Cage<Animal> animalCage = ...; This is a cage designed to hold all kinds of animals, mixed together. It must have bars strong enough to hold in the lions, and spaced closely enough to hold in the butterflies. Such a cage might not even be feasible to build, but if it is, then: animalCage.add(king); animalCage.add(monarch); Since a lion is a kind of animal (Lion is a subtype of Animal), the question then becomes, "Is a lion cage a kind of animal cage? Is Cage<Lion> a subtype of Cage<Animal>?". By the above definition of animal cage, the answer must be "no"! 9