Generics
12/28/2015
Hsuan-Tien Lin (林軒田) htlin@csie.ntu.edu.tw
Department of Computer Science
& Information Engineering
National Taiwan University
( 國立台灣大學資訊工程系)
How can we write a class for an In-
teger set of arbitrary size?
How can we write a class for a
String set of arbitrary size?
How can we write classes for Inte-
ger/String/Double/Professor sets of
arbitrary size?
How can we write one class for ar-
bitrary sets of arbitrary size?
Motivation of Generics (1/3)
1 c l a s s S t r i n g A r r a y {
2 p r i v a t e S t r i n g [ ] myarr ;
3 p u b l i c S t r i n g A r r a y (i n t l e n ) { myarr = new S t r i n g [ l e n ] ; }
4 p u b l i c S t r i n g g e t (i n t n ) { r e t u r n myarr [ n ] ; }
5 p u b l i c v o i d s e t (i n t n , S t r i n g s ) { myarr [ n ] = s ; }
6 p u b l i c v o i d s h o w A l l ( ) {
7 f o r(i n t i = 0 ; i <myarr . l e n g t h ; i ++)
8 System . o u t . p r i n t l n ( myarr [ i ] ) ;
9 }
10 }
11 c l a s s P r o f e s s o r A r r a y {
12 p r i v a t e P r o f e s s o r [ ] myarr ;
13 p u b l i c P r o f e s s o r A r r a y (i n t l e n ) { myarr = new P r o f e s s o r [ l e n ] ; }
14 p u b l i c P r o f e s s o r g e t (i n t n ) { r e t u r n myarr [ n ] ; }
15 p u b l i c v o i d s e t (i n t n , P r o f e s s o r p ) { myarr [ n ] = p ; }
16 p u b l i c v o i d s h o w A l l ( ) {
17 f o r(i n t i = 0 ; i <myarr . l e n g t h ; i ++)
18 System . o u t . p r i n t l n ( myarr [ i ] ) ;
19 }
20 }
•
Can we avoid writing the same boring things again and again?Motivation of Generics (2/3)
1 c l a s s O b j e c t A r r a y {
2 p r i v a t e O b j e c t [ ] myarr ;
3 p u b l i c O b j e c t A r r a y (i n t l e n ) { myarr = new O b j e c t [ l e n ] ; }
4 p r o t e c t e d O b j e c t g e t (i n t n ) { r e t u r n myarr [ n ] ; }
5 p r o t e c t e d v o i d s e t (i n t n , O b j e c t o ) { myarr [ n ] = o ; }
6 p u b l i c v o i d s h o w A l l ( ) {
7 f o r(i n t i = 0 ; i <myarr . l e n g t h ; i ++)
8 System . o u t . p r i n t l n ( myarr [ i ] ) ;
9 }
10 }
11
12 c l a s s S t r i n g A r r a y extends O b j e c t A r r a y {
13 p u b l i c S t r i n g A r r a y (i n t l e n ) { super( l e n ) ; }
14 p u b l i c S t r i n g g e t (i n t n ) { r e t u r n ( S t r i n g )super. g e t ( n ) ; }
15 p u b l i c v o i d s e t (i n t n , S t r i n g s ) { super. s e t ( n , s ) ; }
16 }
•
Yes, by inheritance and polynormphism—everything is an ObjectMotivation of Generics (3/3)
1 c l a s s ANYArray {
2 p r i v a t e ANY [ ] myarr ;
3 p u b l i c ANYArray (i n t l e n ) { myarr = new ANY [ l e n ] ; }
4 p r o t e c t e d ANY g e t (i n t n ) { r e t u r n myarr [ n ] ; }
5 p r o t e c t e d v o i d s e t (i n t n , ANY o ) { myarr [ n ] = o ; }
6 p u b l i c v o i d s h o w A l l ( ) {
7 f o r(i n t i = 0 ; i <myarr . l e n g t h ; i ++)
8 System . o u t . p r i n t l n ( myarr [ i ] ) ;
9 }
10 }
•
Yes, by identifying the common parts, and then replacing•
sed ’s/ANY/String/’ ANYArray.java >StringArray.java
C++ Solution (roughly)
1 t e m p l a t e <c l a s s ANY>
2 c l a s s A r r a y {
3 p r i v a t e ANY [ ] myarr ;
4 p u b l i c A r r a y (i n t l e n ) { myarr = new ANY [ l e n ] ; }
5 p r o t e c t e d ANY g e t (i n t n ) { r e t u r n myarr [ n ] ; }
6 p r o t e c t e d v o i d s e t (i n t n , ANY o ) { myarr [ n ] = o ; }
7 p u b l i c v o i d s h o w A l l ( ) {
8 f o r(i n t i = 0 ; i <myarr . l e n g t h ; i ++)
9 System . o u t . p r i n t l n ( myarr [ i ] ) ;
10 }
11 }
12
13 {
14 Array < S t r i n g > s a r r ( 5 ) ;
15 s a r r . s e t ( 3 , " l a l a l a " ) ;
16 }
•
basically, the step sed ’s/ANY/String/’ ANYArray.cpp >StringArray.cppdone by compiler
•
code automaticallyduplicates during complication as you use
Array<String>, Array<Integer>, Array<Double>, ...Java Solution (roughly)
1 c l a s s Array <ANY> {
2 p r i v a t e ANY [ ] myarr ;
3 p u b l i c A r r a y (i n t l e n ) { myarr = (ANY [ ] ) (new O b j e c t [ l e n ] ) ; }
4 p r o t e c t e d ANY g e t (i n t n ) { r e t u r n myarr [ n ] ; }
5 p r o t e c t e d v o i d s e t (i n t n , ANY o ) { myarr [ n ] = o ; }
6 p u b l i c v o i d s h o w A l l ( ) {
7 f o r(i n t i = 0 ; i <myarr . l e n g t h ; i ++)
8 System . o u t . p r i n t l n ( myarr [ i ] ) ;
9 }
10 }
11
12 {
13 Array < S t r i n g > s a r r ( 5 ) ;
14 s a r r . s e t ( 3 , " l a l a l a " ) ;
15 }
•
the ANY → Object step is automatically done by compiler: a trueone-class solution
How does duplicating solution com-
pare with one-class solution?
How can we write one class for ar-
bitrary sets of arbitrary size while
keeping type information?
Should StringSet extend Object-
Set?
Java Solution: Generics (since 1.4)
•
no manual duplicating (as opposed to old languages): save coding efforts•
no automatic duplicating (as opposed to C++): save code size and re-compiling efforts•
check type information very strictly by compiler (as opposed to single-object polymorphism): ensure type safety in JVMNote: type information
erased after
compilationType Erasure: Mystery 1
1 c l a s s Set <T> {
2 Set ( ) {
3 T [ ] a r r = new T [ 1 0 ] ;
4 a r r [ 0 ] = new T ( ) ;
5 }
6 }
•
cannot new with an “undetermined type” T (no T in runtime)Type Erasure: Mystery 2
1 c l a s s Set <T> {
2 }
3 p u b l i c c l a s s Fun {
4 p u b l i c s t a t i c v o i d main ( S t r i n g [ ] argv ) {
5 Set < S t r i n g > [ ] a r r = new Set < S t r i n g > [ 2 0 ] ;
6 a r r [ 0 ] . addElement (new I n t e g e r ( 3 ) ) ;
7 }
8 }
•
cannot create generic array (after type erasure, no type guarantee)Use of Generics: Java Collection Framework
•
interfaces: Collection (Set, List) and Map•
abstract classes: AbstractCollection (AbstractSet, AbstractList) and AbstractMap•
concrete classes: HashSet, ArrayList, HashMapThis is Where It Starts...
•
array “direct inheritance” introduces type unsafety1 F r u i t [ ] f r A r r = new F r u i t [ 3 ] ;
2 Food [ ] f o A r r = f r A r r ; / / Compiler : Yes !
3 f o A r r [ 0 ] = new Meat ( ) ;/ / Compiler : Yes !
4 F r u i t f r = f r A r r [ 0 ] ; / / Compiler : Yes !
•
generic:not using direct inheritance
1 A r r a y L i s t < F r u i t > f r A r r = new A r r a y L i s t < F r u i t > ( 3 ) ;
2 A r r a y L i s t <Food> f o A r r = f r A r r ; / / Compiler : No !
3 f o A r r . add ( 0 , new Meat ( ) ) ; / / Compiler : Yes !
4 F r u i t f r = f r A r r . g e t ( 0 ) ; / / Compiler : Yes !
What If We Want to “Convert From” An Existing List?
1 MyList < F r u i t > f r A r r = new MyList < F r u i t > ( ) ;
2 MyList <Food> f o A r r = new MyList <Food > ( f r A r r ) ; / / c o n v e r t
3
4 c l a s s MyList <T> {
5 p u b l i c MyList <T > ( ) { }
6 p u b l i c <S> MyList <T> ( MyList <S> i n p u t ) { / ∗ code ∗ / }
7 }
•
shouldn’t work because we can convert Fruit (S) List to Meat (T) List arbitrarily•
enforce type compatibility: S extends T1 p u b l i c <S extends T> MyList <T> ( MyList <S> i n p u t ) { }
2 / / or , i f S i s n o t needed
3 p u b l i c MyList <T> ( MyList <? extends T> i n p u t ) { }
•
now this converting is like “upper cast”What If We Want to “Convert To” A New List?
1 MyList < F r u i t > f r A r r = new MyList < F r u i t > ( ) ;
2 MyList <Food> f o A r r = new MyList <Food > ( ) ;
3 f r A r r . dumpInto ( f o A r r ) ;
4
5 c l a s s MyList <T> {
6 p u b l i c MyList <T > ( ) { }
7 p u b l i c <S> dumpInto ( MyList <S> o u t p u t ) { / ∗ code ∗ / }
8 }
•
shouldn’t work because we can dump Fruit (S) List to Meat (T) List arbitrarily•
enforce type compatibility: S super T1 p u b l i c <S super T> dumpInto ( MyList <S> o u t p u t ) { }
2 / / or , i f S i s n o t needed
3 p u b l i c dumpInto ( MyList <? super T> o u t p u t ) { }
•
again, “upper cast”, but in another directionMore about Type Erasure
•
one main reason: JVM doesn’t need changing between 1.4 and 1.5•
replace types by upper bounds•
cast automatically inserted with safe check•
allow using “legacy code”unsafely
•
List
•
List<Object>
•
List<?>
•
List<Fruit>
Calling legacy code from generic code is inherently dangerous;
once you mix generic code with non-generic legacy code, all the safety guarantees that the generic type system usually provides are void.
•
cons: lose run-time ability of generic types (new T())More on Polymorphism
•
ad-hoc polymorphism: one method name, many different uses (via signature): print(int) and print(Object)•
subtype polynorphism (often seen in OOP): one entry point (parent method), many different future uses (via overriding the method): String.valueOf(Object) which callsObject.toString()
•
parametric polymorphism (generics, often seen in Functional Programming): one class (type-erased), many different uses:printList(List<T> lst)