Download presentation
Presentation is loading. Please wait.
Published byΓαλήνη Χατζηιωάννου Modified over 6 years ago
1
CSE 331 Software Design and Implementation
Lecture 14 Generics⟨1⟩ Leah Perlmutter / Summer 2018
2
Announcements
3
Announcements Quiz 5 is due Thursday Homework 6 due Thursday
Midterm grades and feedback will be out this evening
4
Generics
5
Outline (lec14 and lec15) Basics of generic types for classes and interfaces Basics of bounding generics Generic methods [not just using type parameters of class] Generics and subtyping Using bounds for more flexible subtyping Using wildcards for more convenient bounds Digression: Java’s unsoundness(es) Java realities: type erasure
6
Varieties of abstraction
Abstraction over computation: procedures (methods) int x1, y1, x2, y2; Math.sqrt(x1*x1 + y1*y1); Math.sqrt(x2*x2 + y2*y2); Abstraction over data: Data structures Point p1, p2; Abstraction over implementations: Specifications x >= 0 square root of x Abstraction over types: polymorphism (generics) Point<Integer>, Point<Double> Today!
7
Why we ♥ abstraction Hide details Avoid distraction
Permit details to change later Give a meaningful name to a concept Permit reuse in new contexts Avoid duplication: error-prone, confusing Save reimplementation effort Helps to “Don’t Repeat Yourself” polymorphism
8
Related abstractions interface ListOfStrings { boolean add(String elt); String get(int index); } interface ListOfNumbers { boolean add(Number elt); Number get(int index);
9
Related abstractions interface ListOfStrings { boolean add(String elt); String get(int index); } interface ListOfNumbers { boolean add(Number elt); Number get(int index); … and many, many more // Type abstraction // abstracts over element type E interface List<E> { boolean add(E n); E get(int index); Type abstraction lets us use these types: List<String> List<Number> List<Integer> List<List<String>> …
10
Formal parameter vs. type parameter
Declares a new variable elt, called a (formal) parameter Instantiate by passing in an argument interpretable as Integer E.g., lst.add(7) Scope of elt (declared in method header) is the entire method body interface ListOfIntegers { boolean add(Integer elt); Integer get(int index); } interface List<E> { boolean add(E n); E get(int index); When you declare it in the method header, it’s a parameter When you pass in a value to instantiate it, it’s an argument Take questions Neighbors activity (if not enough questions) Explain in your own words, what is a type parameter? then take questions Declares a new type variable E, called a type parameter Instantiate by passing in an argument interpretable as any reference type E.g., List<String> Scope of E (declared in class header) is the entire class
11
Scope Declaration class NewSet<E> implements Set<E> { // rep invariant: // non-null, contains no duplicates // … List<E> theRep; E lastItemInserted; … } Use Use Use
12
Declaring and instantiating generics
Parameter Parameter class MyClass<TypeVar1, …, TypeVarN> {…} interface MyInterface<TypeVar1, …, TypeVarN> {…} Convention: Type variable has one-letter name such as: T for Type, E for Element, K for Key, V for Value, … To instantiate a generic class/interface, client supplies type arguments: MyClass<String, …, Date> = new MyClass<>(); In general you’ve been taught that one-letter names are a bad idea With generics it’s conventional to use one-letter names, because in most cases you only have one or two type parameters to keep track of at any given time Argument Argument Argument
13
Restricting instantiations by clients
method parameter’s type restricts which arguments can be passed in boolean add1(Object elt); boolean add2(Number elt); add1(new Date()); // OK add2(new Date()); // compile-time error interface List1<E extends Object> {…} interface List2<E extends Number> {…} List1<Date> // OK, Date is a subtype of Object List2<Date> // compile-time error, Date is not a // subtype of Number type parameter’s upper bound restricts which type arguments can be passed in
14
Declaring and instantiating generics: syntax with bounds
class MyClass<TypeVar1 extends TypeBound1, ..., TypeVarN extends TypeBoundN> {…} (same for interface definitions) (default upper bound is Object) To instantiate a generic class/interface, client supplies type arguments: MyClass<String, …, Date> Compile-time error if type is not a subtype of the upper bound
15
Using type variables Code can perform any operation permitted by the bound Because we know all instantiations will be subtypes! class Foo1<E extends Object> { void m(E arg) { arg.asInt(); // compiler error, E might not // support asInt() } class Foo2<N extends Number> { void m(N arg) { arg.asInt(); // OK, since Number and its // subtypes support asInt()
16
More bounds <TypeVar extends SuperType>
One upper bound; accepts given supertype or any of its subtypes <TypeVar extends ClassA & InterfaceB & InterfaceC & …> Multiple upper bounds (superclass/interfaces) with & accepts an argument that matches all the bounds public class TreeSet<T extends Comparable<T>> {...} Recursively-defined bounds TreeSet accepts any type that can be compared to itself - in TreeSet, the type parameter T is passed in as an argument to Comparable
17
Outline Basics of generic types for classes and interfaces
Basics of bounding generics Generic methods [not just using type parameters of class] Generics and subtyping Using bounds for more flexible subtyping Using wildcards for more convenient bounds Digression: Java’s unsoundness(es) Java realities: type erasure
18
Generic Methods
19
Generic classes are not enough
class Utils { public static double sumList(List<Number> lst) { double result = 0.0; for (Number n : lst) { result += n.doubleValue(); } return result; public static Object choose(List<Object> lst) { int i = … // random number < lst.size return lst.get(i); Cannot pass List<Double> Do you feel comfortable with the “static” keyword in the method signature? Thumbs. static means that the method is not being called on an instance of the class theoretically, the method could be outside of any class, but Java needs everything to be in a class it’s not a very object-oriented thing to do, but static methods do appear occasionally You’ve seen an example of a static method: the main method Cannot pass List<Kitten> List<Double> is not a subtype of List<Number> ! We will see why soon. Reminder: static means “no receiver (this parameter)”.
20
Weaknesses of generic classes
Would like to use sumList for any subtype of Number For example, Double or Integer But as we will see, List<Double> is not a subtype of List<Number> Would like to use choose for any element type i.e., any subclass of Object Want to tell clients more about return type than Object Class Utils is not generic, but the methods should be generic
21
Generic methods solve the problem
error: cannot find symbol: class T1 class Utils { public static double result = 0.0; for (Number n : lst) { // T1 also works result += n.doubleValue(); } return result; int i = … // random number < lst.size return lst.get(i); double sumList(List<T1> lst){ Need to ensure T1 subtype of Number error: cannot find symbol: class T2 T2 choose(List<T2> lst) {
22
Generic methods solve the problem
class Utils { public static double result = 0.0; for (Number n : lst) { // T1 also works result += n.doubleValue(); } return result; int i = … // random number < lst.size return lst.get(i); double sumList(List<T1> lst){ <T1 extends Number> <T2> T2 choose(List<T2> lst) {
23
Generic methods solve the problem
class Utils { public static <T1 extends Number> double sumList(List<T1> lst){ double result = 0.0; for (Number n : lst) { // T1 also works result += n.doubleValue(); } return result; public static <T2> T2 choose(List<T2> lst) { int i = … // random number < lst.size return lst.get(i); Insert a type parameter declaration in the method header! What if T1 and T2 had the same name? - Discuss with your neighbor
24
Generic methods solve the problem
class Utils { public static <T1 extends Number> double sumList(List<T1> lst){ double result = 0.0; for (Number n : lst) { // T1 also works result += n.doubleValue(); } return result; public static <T2> T2 choose(List<T2> lst) { int i = … // random number < lst.size return lst.get(i); Scope of T1 is the body of sumList Scope of T2 is the body of choose
25
Using generics in methods
Instance methods can use type parameters of the class Instance methods and static methods can have their own type parameters Generic methods Callers to generic methods need not explicitly instantiate the methods’ type parameters Compiler usually figures it out for you Type inference
26
More examples <T extends Comparable<T>> T max(Collection<T> c) { … } <T extends Comparable<T>> void sort(List<T> list) { // … use list.get() and T’s compareTo (This one works, but we will make it even more useful later by adding more bounds.) <T> void copyTo(List<T> dst, List<T> src) { for (T t : src) dst.add(t);
27
Outline Basics of generic types for classes and interfaces
Basics of bounding generics Generic methods [not just using type parameters of class] Generics and subtyping Using bounds for more flexible subtyping Using wildcards for more convenient bounds Digression: Java’s unsoundness(es) Java realities: type erasure
28
Generics and Subtyping
29
Generics and subtyping
Number List<Number> ? Integer List<Integer> Integer is a subtype of Number Is List<Integer> a subtype of List<Number>? Use subtyping rules (stronger, weaker) to find out…
30
List<Number> and List<Integer>
? List<Integer> List<Number> ? interface List<T> { boolean add(T elt); T get(int index); } So type List<Number> has: boolean add(Number elt); Number get(int index); So type List<Integer> has: boolean add(Integer elt); Integer get(int index); Java subtyping is invariant with respect to generics Neither List<Number> nor List<Integer> subtype of other Not covariant and not contravariant - Subtype needs stronger spec than super - Stronger method spec has: - weaker precondition - stronger postcondition Pair-share - Discuss this with neighbors - Share thoughts/questions with group - relationship of List<T1> to List<T2> not covariant with relationship of T1 to T2 - will be illuminated in the next few slides.
31
Generics and subtyping
If T2 and T3 are different types, then for all Foo, Foo<T2> is not a subtype of Foo<T3> Previous example shows why: Observer method prevents one direction Mutator/producer method prevents the other direction If our types have only observers or only mutators, then one direction of subtyping would be sound Java’s type system is not expressive enough to allow this
32
Read-only allows covariance (in theory)
Number ROList<Number> interface ReadOnlyList<T> { T get(int index); } Type ReadOnlyList<Number> has method: Number get(int index); Type ReadOnlyList<Integer> has method: Integer get(int index); So covariant subtyping would be correct: ROList<Integer> is a subtype of ROList<Number> Covariant = type of ROList<T> changes the same way T changes The Java type system conservatively disallows this subtyping Integer ROList<Integer> - Subtype method needs: - weaker pre - stronger post Covariance: T2 subtype of T1 implies ROList<T2> subtype of ROList<T1> - the arrows point the same direction in the class inheritance diagram
33
Write-only allows contravariance (in theory)
Number WOList<Integer> interface WriteOnlyList<T> { boolean add(T elt); } Type WriteOnlyList<Number> has method: boolean add(Number elt); Type WriteOnlyList<Integer> has method: boolean add(Integer elt); So contravariant subtyping would be correct: WOList<Number> is a subtype of WOList<Integer> Contravariant = type of ROList<T> changes opposite to T The Java type system conservatively disallows this subtyping Integer WOList<Number> - Subtype method needs: - weaker pre - stronger post Contravariance: T2 subtype of T1 implies ROList<T2> supertype of ROList<T1> If these two interfaces existed, there would be nothing to stop you from implementing both of them in the same class. Java can’t enforce read-only or write-only policies (Java is not smart enough to protect you from yourself, so Java does not allow this kind of subtyping)
34
Generic types and subtyping
List<Integer> and List<Number> are not subtype-related Generic types can have subtyping relationships Example: If HeftyBag extends Bag, then HeftyBag<Integer> is a subtype of Bag<Integer> HeftyBag<Number> is a subtype of Bag<Number> HeftyBag<String> is a subtype of Bag<String> … - Subtyping relationships with generics are based on the base type, not the generic parameter
35
Outline Basics of generic types for classes and interfaces
Basics of bounding generics Generic methods [not just using type parameters of class] Generics and subtyping Using bounds for more flexible subtyping Using wildcards for more convenient bounds Digression: Java’s unsoundness(es) Java realities: type erasure Stay tuned for lec15!
36
Announcements
37
Announcements Quiz 5 is due Thursday Homework 6 due Thursday
Midterm grades and feedback will be out this evening Thank you for coming to class today!
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.