Title: bung Softwareentwicklung 2 fr Wirtschaftsinformatik
1Übung Softwareentwicklung 2für
Wirtschaftsinformatik
UE04Generics
mit freundlicher Unterstützung des SSW
2Generics
Object
ListltObjectgt
String
ListltStringgt
- ListltStringgt listString new ArrayListltStringgt()
//1 - ListltObjectgt listObject listString //2
- FRAGE Ist eine Liste von Strings eine Liste von
Objekten?
listObject.add(new Object()) // 3
listObject.add(new String()) // 4
listString.add(new String()) // 5
listString.add(new Object()) // 6
String s listString.get(0) //
7 s.concat("Hugo")
gt attempts to assign an Object to a String!
gt would fail, since get(0) liefert ein Object!
Wenn A ein Subtyp von B is und G ist eine
Gernerische Typ-Deklaration, dann ist GltAgt NICHT
ein Subtyp von GltBgt!
3Generics
- void printCollection(Collection c)
- Iterator i c.iterator()
- for (k 0 k lt c.size() k)
- System.out.println(i.next())
-
-
ACHTUNG Ungetyp sollte vermieden werden!
Collections für beliebige Typen?!
Versuch void printCollection(CollectionltObjectgt
c) for (Object e c) System.out.println(
e)
ABER CollectionltObjectgt, is wie zuvor gezeigt
kein Supertyp von allen Collections!
4Generics
- void printCollection(Collection c)
- Iterator i c.iterator()
- for (k 0 k lt c.size() k)
- System.out.println(i.next())
-
-
- Richtig
- void printCollection(Collectionlt?gt c)
- for (Object e c)
- System.out.println(e)
-
-
- ? wildcard type steht für jeden beliebigen Typ
ACHTUNG Ungetyp sollte vermieden werden!
5Generics
- void printCollection(Collectionlt?gt c)
- for (Object e c) // anything you get is an
Object - System.out.println(e)
-
ABER Collectionlt?gt c new ArrayListltStringgt()
c.add(new Object())
// compile time error
c ist eine Collection unbekannten Typs. Die
add()-Methode ist nur zulässig mit Objekten des
Typs mit dem die Collection definiert wurde. Dies
ist allerings ? (unbekannt). Die add()-Methode
kann aber nur Sub-Typen eines unbekannten Typen
übernehmen und da wir diesen Typen nicht kennen
können ist dies nicht zulässig. Der einzige
zulässige Parameter wäre null, da null von jedem
Typ ist.
6Generics
- public abstract class Shape
- public abstract void draw(Canvas c)
-
- public class Circle extends Shape
- private int x, y, radius
- public void draw(Canvas c) ...
-
- public class Rectangle extends Shape
- private int x, y, width, height
- public void draw(Canvas c) ...
Soll für alle Shapes gehen!
These classes can be drawn on a canvas public
class Canvas public void draw(Shape s)
s.draw(this) public void
drawAll(ListltShapegt shapes) for (Shape s
shapes) s.draw(this)
ABER drawAll läßt sich nur mit einer Liste genau
vom Typ Shape aufrufen
7Generics
- public abstract class Shape
- public abstract void draw(Canvas c)
-
- public class Circle extends Shape
- private int x, y, radius
- public void draw(Canvas c) ...
-
- public class Rectangle extends Shape
- private int x, y, width, height
- public void draw(Canvas c) ...
Typisiert mit einem Typ, der zumindes Shapes ist
(Shapes oder Sub-Klass davon)
? extends ermöglicht das Spezifizieren eines
Parameters, der eine Liste von Shapes oder eines
Subtyps davon sein muss.
These classes can be drawn on a canvas public
class Canvas public void draw(Shape s)
s.draw(this) public void drawAll(Listlt?
extends Shapegt shapes) for (Shape s shapes)
s.draw(this)
8Generics
- public void addRectangle(Listlt? extends Shapegt
shapes) - shapes.add(new Rectangle())
-
// compile-time error!
- Weil addRectangle() könnte ja auch mit einer
Liste von Circles aufgerufen werden! - ListltShapegt anyShapesList // ok
- ListltRectanglesgt recShapesList // ok
- ListltCirclesgt circlesShapesList // NOT OK!
9Generics
- Generic Methods
- Beispiel Methode die ein Array von Objekten nimmt
und dieses in eine Collection eines beliebigen
Typs gibt. - static void fromArrayToCollection(Object a,
Collectionlt?gt c) - for (Object o a)
- c.add(o)
-
-
// compile time error
WEIL Objekte nicht in eine Collections eines
unbekannten Typs eingefügt werden können. Car
cars new Car("VW") new Car("'Audi") Collectio
nltCargt carsCol fromArrayToCollection(car,
carsCol) Car c1 carsCol.get() // Problem, da
hier ein Object gelesen wird
10Generics
- Generic Methods
- Beispiel Methode die ein Array von Objekten nimmt
und dieses in eine Collection eines beliebigen
Typs gibt. - static ltTgt void fromArrayToCollection(T a,
CollectionltTgt c) - for (T o a)
- c.add(o) // correct
-
-
- Car cars new Car("VW") new Car("'Audi")
- CollectionltCargt carsCol
- fromArrayToCollection(car, carsCol)
- Car c1 carsCol.get() // Kein Problem, da hier
ein sicher ein Car gelesen wird
11Generic for Contructor Calls?!
- Because of erasure, ListltIntegergt and
ListltStringgt are the same class, and the compiler
only generates one class when compiling ListltVgt
(unlike in C). As a result, the compiler
doesn't know what type is represented by V when
compiling the ListltVgt class, and so you can't do
certain things with a type parameter (the V in
ListltVgt) within the class definition of ListltVgt
that you could do if you knew what class was
being represented. - Because the runtime cannot tell a ListltStringgt
from a ListltIntegergt (at runtime, they're both
just Lists), constructing variables whose type is
identified by a generic type parameter is
problematic. This lack of type information at
runtime poses a problem for generic container
classes and for generic classes that want to make
defensive copies.
class FooltTgt public void doSomething(T param)
...
public void doSomething(T param) T copy new
T(param) // illegal
12Generic for Contructor Calls?!
- Consider the generic class Foo
- Suppose the doSomething() method wants to make a
defensive copy of the param argument on entry?
You don't have many options. You'd like to
implement doSomething() like this
class FooltTgt public void doSomething(T param)
...
public void doSomething(T param) T copy new
T(param)
// illegal
- But you cannot use a type parameter to access a
constructor because, at compile time, you don't
know what class is being constructed and
therefore what constructors are available. - There's no way to express a constraint like "T
must have a copy constructor" (or even a no-arg
constructor) using generics, so accessing
constructors for classes represented by generic
type parameters is out.
13Using clone()?!
- What about clone()?
- Let's say Foo was defined to make T extend
Cloneable
class FooltT extends Cloneablegt public void
doSomething(T param) T copy (T)
param.clone()
// illegal
- Unfortunately, you still can't call
param.clone(). - Why?
- Because clone() has protected access in Object
and, to call clone(), you have to call it through
a reference to a class that has overridden
clone() to be public. - But T is not known to redeclare clone() as
public, so cloning is also out.
14Using Wildcards?!
- What about wildcard types?
- Suppose you want to make a defensive copy of a
parameter whose type is Setlt?gt. You know that Set
has a copy constructor. You've also been told
it's better to use Setlt?gt instead of the raw type
Set when you don't know the type of the set's
contents, because that approach is likely to emit
fewer unchecked conversion warnings. So you try
this
class Foo public void doSomething(Setlt?gt set)
Setlt?gt copy new HashSetlt?gt(set)
// illegal
- Unfortunately, you can't invoke a generic
constructor with a wildcard type argument, even
though you know such a constructor exists.
- However, you can do this
- This construction is not the most obvious, but it
is type-safe and will do what you think new
HashSetlt?gt(set) would do.
class Foo public void doSomething(Setlt?gt set)
Setlt?gt copy new HashSetltObjectgt(set)
15Constructing Arrays
- How would you implement ArrayListltVgt?
- The ArrayList class is supposed to manage an
array of V, so you might expect the constructor
for ArrayListltVgt to create an array of V
class ArrayListltVgt private V backingArray
public ArrayList() backingArray new
VDEFAULT_SIZE
// illegal
- But this code does not work -- you cannot
instantiate an array of a type represented by a
type parameter! - The compiler doesn't know what type V really
represents, so it cannot instantiate an array of
V.
16The "Dirty Trick" Used in Collection Classes
- The Collections classes use an ugly trick to get
around this problem, one that generates an
unchecked conversion warning when the Collections
classes are compiled. The constructor for the
real implementation of ArrayList looks likes
this - Why does this code not generate an
ArrayStoreException (indicate that an attempt has
been made to store the wrong type of object into
an array of objects) when backingArray is
accessed? - After all, you can't assign an array of Object to
an array of String.
class ArrayListltVgt private V backingArray
public ArrayList() backingArray (V)
new ObjectDEFAULT_SIZE
- Well, because generics are implemented by
erasure, the type of backingArray is actually
Object, because Object is the erasure of V.
This means that the class is really expecting
backingArray to be an array of Object anyway, but
the compiler does extra type checking to ensure
that it contains only objects of type V. - So this approach will work, but it's ugly, and
not really something to emulate (even the authors
of the generified Collections framework say so).
17Passing Type
- The best approach would be to pass a class
literal (Foo.class) into the constructor, so that
the implementation could know, at runtime, the
value of T. - The reason this approach was not taken was for
backward compatibility -- then the new generified
collection classes would not be compatible with
previous versions of the Collections framework. - Here's how ArrayList would have looked under this
approach
public class ArrayListltVgt implements ListltVgt
private V backingArray private ClassltVgt
elementType public ArrayList(ClassltVgt
elementType) this.elementType elementType
backingArray (V) Array.newInstance(elementT
ype, DEFAULT_LENGTH)
- But wait! There's still an ugly, unchecked cast
there when calling Array.newInstance(). - Why? Again, it's because of backward
compatibility. The signature of
Array.newInstance() is
public static Object newInstance(Classlt?gt
componentType, int length)
public staticltTgt T newInstance(ClassltTgt
componentType, int length)
- All to preserve backward compatibility. To create
an array of a primitive type, such as int, you
call Array.newInstance() with the TYPE field from
the appropriate wrapper class (in the case of
int, you would pass Integer.TYPE as the class
literal). Generifying Array.newInstance() with a
parameter of ClassltTgt instead of Classlt?gt would
have been more type-safe for reference types, but
would have made it impossible to use
Array.newInstance() to create an instance of a
primitive array. Perhaps in the future, an
alternate version of newInstance() will be
provided for reference types so you can have it
both ways.