Post on 19-Jan-2016
transcript
Advanced Generics and other niceties
SOFTENG 251
Object Oriented Software Construction
SOFTENG 251 Object Oriented Software Construction 2Advanced generics and niceties
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 3Advanced generics and niceties
Subtyping
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());}
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
Item
Book Movie CD
<<interface>>
Priced
SOFTENG 251 Object Oriented Software Construction 4Advanced generics and niceties
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 5Advanced generics and niceties
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()); }}
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
SOFTENG 251 Object Oriented Software Construction 6Advanced generics and niceties
Subtyping in generics
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"));}
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!
SOFTENG 251 Object Oriented Software Construction 7Advanced generics and niceties
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 8Advanced generics and niceties
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()); }}
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 an unbounded wildcard Note: <?> is equivalent to <? extends Object>
SOFTENG 251 Object Oriented Software Construction 9Advanced generics and niceties
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:
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!
List<Book> books = ...; //[book1,book2,book3]List<Items> archive = ...; //[book1,cd1,movie1,book2,cd2]List<Book> newBooks = listDiff(books, archive); //[book3]
SOFTENG 251 Object Oriented Software Construction 10Advanced generics and niceties
Examples of wildcards in API
Realise extends in generics means both extends and implements
public static <T> void sort(List<T> list, Comparator<? super T> c)
public static <T extends Comparable<? super T>> void sort(List<T> list)
CD
<<interface>>
Priced
+getPrice():Money
public class PriceComparatorimplements Comparator<Priced> { public int compare(Priced p1, Priced p2) { return p1.getPrice().compareTo(p2.getPrice()); }}
List<CD> albums = getAlbums();Collections.sort(albums, new PriceComparator());List<Shirt> shirts = getShirts();Collections.sort(shirts, new PriceComparator());
Shirt
This allows us to sort a specific list with a more general comparator, increasing flexibility. E.g:
SOFTENG 251 Object Oriented Software Construction 11Advanced generics and niceties
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)?
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);
Methods in Collection<E>:+addAll(Collection<? extends E>):boolean+containsAll(Collection<?>):boolean+removeAll(Collection<?>):boolean+retainAll(Collection<?>):boolean
Add all elements in items to copyaddAll() expects Collections<? extends Item> and we’re giving in a Set<Movie> all matches up
HashSet<Movie>
items : Set<Movie>
ArrayList<Item>
copy : List<Item>
item1 : Movie
item2 : Movie
item3 : Movie
SOFTENG 251 Object Oriented Software Construction 12Advanced generics and niceties
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
public class ShortTitles implementsSearchCriterion<Movie> { private int length; public ShortTitles(int length) { this.length = length; } public boolean accept(Movie mov) { return mov.getTitle().length() < length; }}
SearchCriterion<Movie> filter = new ShortTitles(5);List<Movie> movies = mdb.listMovies();List<Movie> results = genericSearch(movies, filter);
results : List<Movie> T Movie
TVSeries
Item
tvShows : List<Movie>
filter : SearchCriterion<Movie>
Accept any movie whose title is less than length characters long
SOFTENG 251 Object Oriented Software Construction 13Advanced generics and niceties
Assignment 2 task 5
public static<T> List<T> genericSearch(List<? extends T> items, SearchCriterion<T> filter)
Search List items more specific than T with filter for T, and return a List of T flexibility from one side
results : List<Movie>
tvShows : List<TVSeries>
? extends T
T
public class ShortTitles implementsSearchCriterion<Movie> { private int length; public ShortTitles(int length) { this.length = length; } public boolean accept(Movie mov) { return mov.getTitle().length() < length; }}
SearchCriterion<Movie> filter = new ShortTitles(5);List<TVSeries> tvShows = mdb.listTVShows();List<Movie> results = genericSearch(tvShows, filter);
Movie
TVSeries
Item
filter : SearchCriterion<Movie>
SOFTENG 251 Object Oriented Software Construction 14Advanced generics and niceties
Assignment 2 task 5
public static<T> List<T> genericSearch(List<? extends T> items, SearchCriterion<? super T> filter)
Search List items more specific than T with filter more general than T, and return a List of T flexibility from both sides
Movie
TVSeries
Item
results : List<Movie>
filter : SearchCriterion<Item>
tvShows : List<TVSeries>
? super T
? extends T
T
public class ShortTitles implementsSearchCriterion<Item> { private int length; public ShortTitles(int length) { this.length = length; } public boolean accept(Item item) { return item.getTitle().length() < length; }}
SearchCriterion<Item> filter = new ShortTitles(5);List<TVSeries> tvShows = mdb.listTVShows();List<Movie> results = genericSearch(tvShows, filter);
SOFTENG 251 Object Oriented Software Construction 15Advanced generics and niceties
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 16Advanced generics and niceties
List<Movie> m = new ArrayList<Movie>();...public<T extends Item> void inspect(List<T> v) { T first = v.get(0); String title = first.getTitle();}
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
‘Item’
SOFTENG 251 Object Oriented Software Construction 17Advanced generics and niceties
Type erasure – limitations What does this mean?
1. 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 18Advanced generics and niceties
Type erasure – limitations
2. You can’t use parameterised types for operations that would lose type information at runtime due to erasure
public void failToOverload(List<Movies> movies) { ...}public void failToOverload(List<Book> books) { ...}
public void convert(List rawList) { if (rawList instanceOf List<Item>) { List<Item> items = (List<Item>) rawList; }}
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
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
SOFTENG 251 Object Oriented Software Construction 19Advanced generics and niceties
Type erasure – limitations
3. Also, certain “suspicious” operations are allowed so as to enable interoperability with legacy code
public static void main(String[] args) { List oldBooks = (List)legacyCode(); List<Books> = oldBooks; //COMPILER WARNING}
public static List legacyCode() { List rawList = new ArrayList(); rawList.add(new Book("LOTR","Tolkien")); return rawList;}
If you do not parameterise a generic type, it becomes a raw type. But compiler doesn’t like this (sounds familiar?)
This is actually allowed! (Why?) BUT, the compiler issues a warning, and this practice is generally discouraged (unless there’s no other choice).
SOFTENG 251 Object Oriented Software Construction 20Advanced generics and niceties
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 21Advanced generics and niceties
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
Reference: Anything that extends Object
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); Most Number
subclasses implement Comparable
Primitive: int boolean double ...
SOFTENG 251 Object Oriented Software Construction 22Advanced generics and niceties
Autoboxing (Java 1.5)
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);
new Integer(30);new Double(0.25);
decimal.intValue() + fraction.doubleValue();
new Integer(25)
Translates to:
numbers.get(0).intValue();
Double wrongPrecision = 30;Number n = 20.0;double tooGeneral = n;
Integer danger = null;int ouch = danger;
But note: Although int is a double, Integer is NOT a Double
Although you can call n.doubleValue(), this assignment is only valid if n is a Double
danger.intValue()Guess what happens here…
SOFTENG 251 Object Oriented Software Construction 23Advanced generics and niceties
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");
Enumerated types
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
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";
The role is just a String. Any bogus String value can be given where really a restricted set of names is expected.
We could accidentally make two roles “the same”, leading to unexpected behaviour
SOFTENG 251 Object Oriented Software Construction 24Advanced generics and niceties
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 25Advanced generics and niceties
Java 1.5 enum types
public enum Role { DIRECTOR, PRODUCER, WRITER, EDITOR, CINEMATOGRAPHER, COMPOSER}
Compiling the enum declaration Role generates a fully-fledged class which (amongst other things):
Order of declaration matters 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
SOFTENG 251 Object Oriented Software Construction 26Advanced generics and niceties
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.
DIRECTORPRODUCERWRITEREDITORCINEMATOGRAPHERCOMPOSER
-2
true
DIRECTOR
2
Compilation error
SOFTENG 251 Object Oriented Software Construction 27Advanced generics and niceties
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 28Advanced generics and niceties
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 thisList<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 29Advanced generics and niceties
Vararg usage Vararg T... 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()); }}
public static void printf(String format, Object... args) { ... }
System.out.printf("%d people say %s", 10, "hi");
Hey! Like in C: printf("%d people say %s", 10, "hi");
In fact, Java has one too! System.out.printf();