290 likes | 379 Views
Advanced Generics and other niceties. SOFTENG 251 Object Oriented Software Construction. Announcements. Test official time change: 6:30pm – come in at 6:15 ~ 6:20 to start reading test script (Thursday 24 th ) Wednesday tutorial going over past years’ tests
E N D
Advanced Generics and other niceties SOFTENG 251 Object Oriented Software Construction
Announcements • Test official time change: • 6:30pm – come in at 6:15 ~ 6:20 to start reading test script • (Thursday 24th) • Wednesday tutorial going over past years’ tests • Post questions on Wiki by Tuesday night • Some questions from past years not applicable to this year’s • Assignment 2: aim to get at least first 4 tasks finished and demonstrated by Thursday (24th) lab • Task 5 – submit via dropbox if need be • Optional tasks – can demonstrate by 1st May SOFTENG 251 Object Oriented Software Construction
Subtyping • Remember polymorphism allows reference variables of type T to point to objects of type T or its subclasses • e.g. List := ArrayListItem := MoviePriced := CDItem := CD <<interface>> Priced Item Book Movie CD public static void main(String[] args) { Object obj = "I’m a String, but also an Object"; Movie movie = new Movie("Psycho",1960); printTitle(movie); } public static void printTitle(Item item) { System.out.println(item.getTitle()); } SOFTENG 251 Object Oriented Software Construction
Subtyping in arrays • Can an array of T point to an array of T’s subclass? • e.g. Item[] := Movie[] ? • Yes public static void main(String[] args) { Movie[] movies = new Movie[5]; populate(movies); list(movies); } ... public static void list(Item[] items) { for (Item item : items) { System.out.println(item.getTitle()); } } SOFTENG 251 Object Oriented Software Construction
Subtyping in generics • Can a reference variable of type U parameterised with T point to an object of type U (or its subclass) parameterised with T’s subclass? • e.g. List<Item> := List<Movie> or List<Item> := ArrayList<Movie>? • NO…! :-S public static void main(String[] args) { List<Movie> movies = new ArrayList<Movie>(); populate(movies); list(movies); } ... public static void list(List<Item> items) { for (Item item : items) { System.out.println(item.getTitle()); } } ? SOFTENG 251 Object Oriented Software Construction
Subtyping in generics Suppose this was allowed… Then this would also be valid (!)(If we take this method out of context, then all we can see is adding a Book to a vector of Item’s, which seems perfectly valid) Effectively we’d be allowed to add a Book to a vector that’s only supposed to have Movies! public static void main(String[] args) { List<Movie> movies = new ArrayList<Movie>(); populate(movies); list(movies); } ... public static void list(List<Item> items) { ... items.add(new Book("LOTR","Tolkien")); } SOFTENG 251 Object Oriented Software Construction
Subtyping in generics • How can we parameterise a List to accept any subtype of Item (including Item itself)? • One way: public<T extends Item> static void list(List<T> items) • There’s actually another way...... public static void main(String[] args) { List<Movie> movies = new ArrayList<Movie>(); List<Book> books = new LinkedList<Book>(); populate(movies); populate(books); list(movies); list(books); } ... public static void list(List<Item> items) { for (Item item : items) { System.out.println(item.getTitle()); } } ??? SOFTENG 251 Object Oriented Software Construction
Wildcard types • Use wildcards to denote ‘unknown type’ • <? extends T> (i.e. any subclass of T, including T itself) signifies a bounded wildcard • <?>(i.e. any type) signifies anunbounded wildcard • Note:<?> is equivalent to <? extends Object> public static void main(String[] args) { List<Movie> movies = new ArrayList<Movie>(); List<Book> books = new LinkedList<Book>(); populate(movies); populate(books); list(movies); list(books); } ... public static void list(List<? extends Item> items) { for (Item item : items) { System.out.println(item.getTitle()); } } SOFTENG 251 Object Oriented Software Construction
More wildcards • <? super T> is also a bounded wildcard, meaning the reverse of extends, i.e. “any superclass (ancestor) of T, including T itself” • E.g. below: find items in first, but not in second – second can be more general than first: List<Book> books = ...; //[book1,book2,book3] List<Items> archive = ...; //[book1,cd1,movie1,book2,cd2] List<Book> newBooks = listDiff(books, archive); //[book3] public<T> List<T> listDiff(List<T> list1, List<? super T> list2) { List<T> itemsOnlyIn1 = new ArrayList<T>(); for (T item : list1) { if (!list2.contains(item)) { itemsOnlyIn1.add(item); } } return itemsOnlyIn1; } Look, we can use wildcards with type parameters too! SOFTENG 251 Object Oriented Software Construction
<<interface>> Priced +getPrice():Money Examples of wildcards in API Realise extends in generics means both extends and implements public static <T extends Comparable<? super T>> void sort(List<T> list) public static <T> void sort(List<T> list, Comparator<? super T> c) This allows us to sort a specific list with a more general comparator, increasing flexibility. E.g: List<CD> albums = getAlbums(); Collections.sort(albums, new PriceComparator()); List<Shirt> shirts = getShirts(); Collections.sort(shirts, new PriceComparator()); public class PriceComparator implements Comparator<Priced> { public int compare(Priced p1, Priced p2) { return p1.getPrice().compareTo(p2.getPrice()); } } Shirt CD SOFTENG 251 Object Oriented Software Construction
Collection: bulk methods • Collection<? extends E> Collection of E or subclasses • Below: what happens to the elements of the Set items after copy.remove(item1)? Methods in Collection<E>: +addAll(Collection<? extends E>):boolean +containsAll(Collection<?>):boolean +removeAll(Collection<?>):boolean +retainAll(Collection<?>):boolean items : Set<Movie> copy : List<Item> Set<Movie> items = new HashSet<Movie>(); items.add(item1); items.add(item2); items.add(item3); List<Item> copy = new ArrayList<Item>(); copy.addAll(items); copy.remove(item1); HashSet<Movie> ArrayList<Item> item1 : Movie item3 : Movie item2 : Movie Add all elements in items to copy addAll() expects Collections<? extends Item> and we’re giving in a Set<Movie> all matches up SOFTENG 251 Object Oriented Software Construction
Assignment 2 task 5 (kind of) public static<T> List<T> genericSearch(List<T> items, SearchCriterion<T> filter) • Given a List items and a filter, apply the filter to each element in items and return a List of only the elements accepted by the filter SearchCriterion<Movie> filter = new ShortTitles(5); List<Movie> movies = mdb.listMovies(); List<Movie> results = genericSearch(movies, filter); Accept any movie whose title is less than length characters long public class ShortTitles implements SearchCriterion<Movie> { private int length; public ShortTitles(int length) { this.length = length; } public boolean accept(Movie mov) { return mov.getTitle().length() < length; } } Item filter : SearchCriterion<Movie> Movie T results : List<Movie> tvShows : List<Movie> TVSeries SOFTENG 251 Object Oriented Software Construction
Assignment 2 task 5 public static<T> List<T> genericSearch(List<? extends T> items, SearchCriterion<T> filter) • Search List itemsmore specific than T with filter for T, and return a List of T flexibility from one side SearchCriterion<Movie> filter = new ShortTitles(5); List<TVSeries> tvShows = mdb.listTVShows(); List<Movie> results = genericSearch(tvShows, filter); public class ShortTitles implements SearchCriterion<Movie> { private int length; public ShortTitles(int length) { this.length = length; } public boolean accept(Movie mov) { return mov.getTitle().length() < length; } } Item filter : SearchCriterion<Movie> Movie T results : List<Movie> ? extends T tvShows : List<TVSeries> TVSeries SOFTENG 251 Object Oriented Software Construction
Assignment 2 task 5 public static<T> List<T> genericSearch(List<? extends T> items, SearchCriterion<? super T> filter) • Search List itemsmore specific than T with filtermore general than T, and return a List of T flexibility from both sides SearchCriterion<Item> filter = new ShortTitles(5); List<TVSeries> tvShows = mdb.listTVShows(); List<Movie> results = genericSearch(tvShows, filter); public class ShortTitles implements SearchCriterion<Item> { private int length; public ShortTitles(int length) { this.length = length; } public boolean accept(Item item) { return item.getTitle().length() < length; } } Item filter : SearchCriterion<Item> ? super T Movie T results : List<Movie> ? extends T tvShows : List<TVSeries> TVSeries SOFTENG 251 Object Oriented Software Construction
Wildcard caveats • Can’t provide argument for a parameter of wildcard type (except for null) • Can’t instantiate generic types parameterised with a wildcard (but you can declare them) • Think of wildcard-parameterised types as “read-only” types public static void populate(List<? extends Item> items) { items.add(new Movie("Vertigo",1958)); items.add(new Book("Dilbert","Adams")); items.add(null); //COMPILER ALLOWS } public static Vector<?> reverseA(List<? extends Item> list) { List<?> rev = new ArrayList<?>(); ... } SOFTENG 251 Object Oriented Software Construction
‘Item’ Aside: type erasure • Only the compiler knows about generics • The JVM (Java Virtual Machine) doesn’t • Requiring JVM implementations to cater for generics would be impractical (hint: Sun isn’t the only JVM vendor in the market!) • Thus compiler erases information about generic types when generating bytecodes (.class files) to be executed by the JVM • Maintain compatibility with legacy code – i.e. Java programs written before generics were introduced List<Movie> m = new ArrayList<Movie>(); ... public<T extends Item> void inspect(List<T> v) { T first = v.get(0); String title = first.getTitle(); } SOFTENG 251 Object Oriented Software Construction
Type erasure – limitations • What does this mean? • You can’t use type parameters for operations that require runtime knowledge of the exact type being used public<T extends Item> void addItem(List<T> list) { list.add(new T()); } public<T>T[] makeArray(Vector<T> v) { T[] array = new T[5]; for ( int i = ... } public <T extends Item> void yikes(Object obj) { if (obj instanceof T) { T item = (T)obj; } } In all three cases, the JVM would have no knowledge of the exact type that T is supposed to be, so it would not be able to find the appropriate class definition in order to perform instantiation, array creation or casting Compiler issues warning SOFTENG 251 Object Oriented Software Construction
Type erasure – limitations • You can’t use parameterised types for operations that would lose type information at runtime due to erasure List<Item> does not exist in runtime: it’s just plain old List. Again, due to type erasure, the information about the type argument is lost public void convert(List rawList) { if (rawList instanceOf List<Item>) { List<Item> items = (List<Item>) rawList; } } For the same reasons, you can’t overload a method with the same generic type as parameter, even if they are each parameterised with a different type public void failToOverload(List<Movies> movies) { ... } public void failToOverload(List<Book> books) { ... } SOFTENG 251 Object Oriented Software Construction
Type erasure – limitations • Also, certain “suspicious” operations are allowed so as to enable interoperability with legacy code If you do not parameterise a generic type, it becomes a raw type. But compiler doesn’t like this (sounds familiar?) public static List legacyCode() { List rawList = new ArrayList(); rawList.add(new Book("LOTR","Tolkien")); return rawList; } This is actually allowed! (Why?) BUT, the compiler issues a warning, and this practice is generally discouraged (unless there’s no other choice). public static void main(String[] args) { List oldBooks = (List)legacyCode(); List<Books> = oldBooks; //COMPILER WARNING } SOFTENG 251 Object Oriented Software Construction
Further reading • Generics can be pretty daunting and confusing • The best way to understand generics is to keep applying them • The compiler is your judge! • The Java Tutorial on generics: good comprehensive reference • http://java.sun.com/docs/books/tutorial/java/generics/index.html (for “starters”) • http://java.sun.com/docs/books/tutorial/extra/generics/index.html (more comprehensive) SOFTENG 251 Object Oriented Software Construction
Most Number subclasses implement Comparable Autoboxing – prelude • Recall difference between primitive and reference types • Can’t use primitive types in a context expecting only reference types. E.g: • Can’t instantiate List<int>, or call remove(double) on Set • To compensate for this Java provides a “wrapper” class for each primitive type • Plus an abstract class Number to represent all numeric primitives • But wrapping/unwrapping primitives every time can be a pain Primitive: int boolean double ... Reference: Anything that extendsObject Integer decimal = new Integer(30); Double fraction = new Double(0.25); Double sum = new Double(decimal.intValue()+fraction.doubleValue()); System.out.println(sum); System.out.println(decimal.compareTo(new Integer(25))); List<Number> numbers = new ArrayList<Number>(); numbers.add(decimal); numbers.add(fraction); SOFTENG 251 Object Oriented Software Construction
But note: Although int is a double, Integer is NOT a Double Double wrongPrecision = 30; Number n = 20.0; double tooGeneral = n; Integer danger = null; int ouch = danger; Although you can call n.doubleValue(), this assignment is only valid if n is a Double danger.intValue() Guess what happens here… Autoboxing (Java 1.5) Translates to: new Integer(30); new Double(0.25); Integer decimal = 30; Double fraction = 0.25; double sum = decimal + fraction; System.out.println(sum); System.out.println(decimal.compareTo(25)); List<Integer> numbers = ... int num1 = numbers.get(0); int num2 = numbers.get(1); decimal.intValue() + fraction.doubleValue(); new Integer(25) numbers.get(0).intValue(); SOFTENG 251 Object Oriented Software Construction
Java lacked support for enumerated types prior to 1.5, so integer/String constants were commonly used as workaround This approach has problems Not type-safe Error-prone Enumerated types public static final String DIRECTOR = "director"; public static final String PRODUCER = "director"; public static final String WRITER = "writer"; public static final String EDITOR = "editor"; public static final String CINEMATOGRAPHER = "cinematographer"; public static final String COMPOSER = "composer"; Person p1 = ...; Person p2 = ...; Person p3 = ...; Movie movie = ... ; movie.addCrewRole(DIRECTOR, p1); movie.addCrewRole(PRODUCER, p2); movie.addCrewRole("dictator", p3); Person p4 = movie.getCrewRole("bum"); We could accidentally make two roles “the same”, leading to unexpected behaviour The role is just a String. Any bogus String value can be given where really a restricted set of names is expected. SOFTENG 251 Object Oriented Software Construction
Java 1.5 enum types • An enum declaration in Java looks like that of its C, C# and C++ counterparts, but the similarity is only superficial public enum Role {DIRECTOR, PRODUCER, WRITER, EDITOR, CINEMATOGRAPHER, COMPOSER} • An enum declaration can also include additional state and behaviour – and implement further interfaces! • Think of enums as special classes that can only have a limited number of distinct instances usable in switch statements public String describe(Role role) { switch (role) { case DIRECTOR: return "Tells people what to do and does nothing for oneself"; case PRODUCER: return "Gets all the money"; etc... } } SOFTENG 251 Object Oriented Software Construction
Compiling the enum declaration Role generates a fully-fledged class which (amongst other things): Order of declaration matters Java 1.5 enum types • equals() – returns true if two Role objects have the same value • toString() – returns its value as a String • name() – returns its value • ordinal() – returns its position relative the complete possible set of values • values() – (static) returns an array containing all possible Role values • compareTo(Role) – compares the ordinal() beteween this and other • All methods above except toString() are final, meaning they can’t be further overridden public enum Role { DIRECTOR, PRODUCER, WRITER, EDITOR, CINEMATOGRAPHER, COMPOSER } SOFTENG 251 Object Oriented Software Construction
DIRECTOR PRODUCER WRITER EDITOR CINEMATOGRAPHER COMPOSER Compilation error true -2 2 DIRECTOR Java 1.5 enum types public enum Role { DIRECTOR, PRODUCER, WRITER, EDITOR, CINEMATOGRAPHER, COMPOSER } for( Role role : Role.values() ) System.out.println( role ); Role king = Role.DIRECTOR; Role brains = Role.WRITER; Role poet = Role.WRITER; Role err = Role.IDONTEXIST; System.out.println(king.toString()); System.out.println(brains.equals(poet)); System.out.println(brains.ordinal()); System.out.println(king.compareTo(brains)); Note how instances of an enumeration type are created. SOFTENG 251 Object Oriented Software Construction
Method overloading • Java allows several methods of the same name to be defined within a class as long as the methods have different sets of parameters • Contrast to overriding • One parameter set is different to another if at least one of the following is true: • The number of parameters is different • The types of the parameters is different • The ordering of parameter types is different public void println() { ... } public void println(String message) { ... } public void println(String message, int repeat) { ... } public void println(int heading, String message) { ... } public void println(int number) { ... } public void println(Object object) { ... } SOFTENG 251 Object Oriented Software Construction
Vararg – motivation • Test if a list of things have all the expected elements • Iterate through each element and match with expected element • This can get rather tedious – imagine we have to do 20 test cases like this List<String> words = getList(); Iterator<String> iterator = words.iterator(); assertEquals(5, words.size()); assertEquals("first",iterator.next()); assertEquals("second",iterator.next()); assertEquals("third",iterator.next()); assertEquals("fourth",iterator.next()); assertEquals("fifth",iterator.next()); • Wouldn’t it be nice if we can do something like this instead: List<String> words = getList(); assertCollectionEquals(words, "first","second","third","fourth","fifth"); List<String> letters = getAnotherList(); assertCollectionEquals(letters, "x","y","z","b"); SOFTENG 251 Object Oriented Software Construction
Vararg usage • VarargT... represents a variable sequence of arguments of type T • vararg parameter of type T... is equivalent to T[] • Can only come as the last argument to prevent ambiguity public static<T> void assertCollectionEquals(Collection<T> actual, T... expected) { assertEquals(expected.length, actual.size()); Iterator<T> actualIterator = actual.iterator(); for (T expectedElem : expected) { assertEquals(expectedElem,actualIterator.next()); } } • Hey! Like in C: printf("%d people say %s", 10, "hi"); • In fact, Java has one too! System.out.printf(); System.out.printf("%d people say %s", 10, "hi"); public static void printf(String format, Object... args) { ... } SOFTENG 251 Object Oriented Software Construction