Typparameter in Java (Typparameter in Java), Lektion, Seite 722837
https://www.purl.org/stefan_ram/pub/typparameter_java (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram

Typparameter in Java

Bits und Kwits

Wir nennen einen der Werte {0,1} ein Bit und einen der Werte {0,1,2,3} ein Kwit.

Eine Bitquelle (Bitdonator, Leseoperation) ist auch eine Kwitquelle, da wir jedes Bit aus einer Bitquelle auch als ein Kwit ansehen können.

B ⊂ K

Eine Kwitquelle (Kwitdonator, Leseoperation) ist aber keine Bitquelle, denn sie kann auch 2 oder 3 liefern, welche keine Bits sind.

B ⊄ K

Eine Kwitsenke (Kwitakzeptator, Schreiboperation) ist auch eine Bitsenke, da wir jedes Bit auch in eine Kwitsenke schreiben können.

K* ⊂ B*

Eine Bitsenke (Bitakzeptator, Schreiboperation) ist aber keine Kwitsenke, denn sie kann weder 2 noch 3 akzeptiere, welche keine Bits sind.

B* ⊄ K*

Wenn ein Objekt nun gleichzeitig als Bit-Quelle als auch als Bit-Senke dienen soll, dann kann es weder als Kwit-Quelle noch als Kwit-Senke angesehen werden, obwohl jedes Bit eine Quit ist.

Ist ein Quadrat ein Rechteck?

Ein Quadrat scheint ein Rechteck zu sein, da es nur eine spezielle Art von Rechteck ist, bei dem beide Seiten gleich lang sind.

Main.java

class Rectangle
{ int a; int b;
int getA(){ return this.a; }
int getB(){ return this.b; }
void setA( final int a ){ this.a = a; }
void setB( final int b ){ this.b = b; }}

public final class Main
{ public static void main( final java.lang.String args[] )
{ final Rectangle rectangle
= new Rectangle();
rectangle.setA( 2 );
rectangle.setB( 4 );
java.lang.System.out.println( rectangle.getA() );
java.lang.System.out.println( rectangle.getB() ); }}

transcript
2
4

Doch, wenn man auch Schreibzugriffe erlaubt, sieht man, daß es als Rechteck nicht zu gebrauchen ist.

Main.java

class Rectangle
{ int a; int b;
int getA(){ return this.a; }
int getB(){ return this.b; }
void setA( final int a ){ this.a = a; }
void setB( final int b ){ this.b = b; }}

final class Square extends Rectangle
{ int getA(){ return this.a; }
int getB(){ return this.a; }
void setA( final int a ){ this.a = a; }
void setB( final int b ){ this.a = b; }}

public final class Main
{ public static void main( final java.lang.String args[] )
{ final Rectangle rectangle = new Square();
rectangle.setA( 2 );
rectangle.setB( 4 );
java.lang.System.out.println( rectangle.getA() );
java.lang.System.out.println( rectangle.getB() ); }}

transcript
4
4

Man kann das obige Programm noch variieren, aber in jedem Fall wird man entweder in der Erweiterung den Kontrakt der Basisklasse verletzen oder keine richtig sinnvolle Quadratklasse erhalten.

Wenn Lese- und Schreiboperationen zugelassen sind, so ist ein Quadrat kein Rechteck und ein Rechteck kein Quadrat – es handelt sich um zwei unabhängige Typen von denen keiner Untertyp des jeweils anderen ist.

Ein Rechteck hat in unserer Modellierung zwei unabhängige Speicher: einen für die Breite und einen für die Höhe. Da ein Quadrat in unserer Modellierung nur einen einzigen Speicher (für die Länge einer seiner Seiten) hat, ist es als Rechteck nicht zu gebrauchen, denn es kann nur einen Wert speichern und nicht zwei. Also ist ein Quadrat in unserer Modellierung kein Rechteck. Und umgekehrt ist das Rechteck auch kein Quadrat.

Ob ein Rechteck nun ein Quadrat ist, oder ein Quadrat ein Rechteck, oder keines von beidem, das hängt letztendlich immer von der Modellierung ab. Es ist nicht schon im Worte „Rechteck“ oder „Quadrat“ enthalten.

Bei unserer Modellierung können wir ein Quadrat nicht als Rechteck ansehen, weil wir auch Änderungen der Dimensionen zulassen wollen. Daher sind unsere Rechtecke und Quadrate eigentlich Rechteckspeicher und Quadratspeicher. Und ein Quadratspeicher ist kein Rechtecktspeicher. Würde man Rechtecke und Quadrate mathematisch als Punktemengen definieren, so wäre jedes Quadrat ein Rechteck.

Varianz von Generics

Wir hatten schon gesehen, daß die Kovarianz von Reihungen zu Fehlern führt. Aus den genannten Gründen sind die Regelungen für Typparameter invariant.

List<String> ist weder Untertyp von List<Object> noch Obertyp.

Wildcards für Lese-Operationen (kovariant)

Eine Klasse mit einem Typargument einer Unterklasse von »java.lang.Number« ist erlaubt.

Main.java

public final class Main
{

static void m( java.util.List< ? extends java.lang.Number >list )
{ java.lang.System.out.println( list.get( 0 )); }

public static void main( final java.lang.String args[] )
{ final java.util.List< java.lang.Integer > list
= new java.util.ArrayList<>();
list.add( 1 );
m( list ); }}

transcript
1

Eine Klasse mit einem Typargument einer Oberklasse von »java.lang.Number« ist nicht erlaubt.

Main.java

public final class Main
{

static void m( java.util.List< ? extends java.lang.Number >list )
{ java.lang.System.out.println( list.get( 0 )); }

public static void main( final java.lang.String args[] )
{ final java.util.List< java.lang.Object > list
= new java.util.ArrayList<>();
list.add( 1 );
m( list ); }}

transcript
Main.java:11: error: incompatible types: List<Object> cannot be converted to List<? extends Number>
m( list ); }}
^
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error

Schreiben geht nicht.

Main.java

public final class Main
{

static void m( java.util.List< ? extends java.lang.Number >list )
{ java.lang.System.out.println( list.get( 0 ));
list.add( java.lang.Integer.valueOf( 2 )); }

public static void main( final java.lang.String args[] )
{ final java.util.List< java.lang.Integer > list
= new java.util.ArrayList<>();
list.add( 1 );
m( list ); }}

transcript
Main.java:6: error: no suitable method found for add(Integer)
list.add( java.lang.Integer.valueOf( 2 )); }
^
method Collection.add(CAP#1) is not applicable
(argument mismatch; Integer cannot be converted to CAP#1)
method List.add(CAP#1) is not applicable
(argument mismatch; Integer cannot be converted to CAP#1)
where CAP#1 is a fresh type-variable:
CAP#1 extends Number from capture of ? extends Number
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error

Wildcards für Schreib-Operationen (kontravariant)

Schreiben ist erlaubt.

Main.java

public final class Main
{

static void m( java.util.List< ? super java.lang.Number >list )
{ list.add( java.lang.Integer.valueOf( 2 ));
list.add( java.lang.Double.valueOf( 2 )); }

public static void main( final java.lang.String args[] )
{ final java.util.List< java.lang.Object > list
= new java.util.ArrayList<>();
m( list );
java.lang.System.out.println( list.get( 0 )); }}

transcript
2

Schreiben eines Obertypen von Number aber nicht

Main.java

public final class Main
{

static void m( java.util.List< ? super java.lang.Number >list )
{ list.add( new java.lang.Object() ); }

public static void main( final java.lang.String args[] )
{ final java.util.List< java.lang.Object > list
= new java.util.ArrayList<>();
m( list );
java.lang.System.out.println( list.get( 0 )); }}

transcript
Main.java:5: error: no suitable method found for add(Object)
{ list.add( new java.lang.Object() ); }
^
method Collection.add(CAP#1) is not applicable
(argument mismatch; Object cannot be converted to CAP#1)
method List.add(CAP#1) is not applicable
(argument mismatch; Object cannot be converted to CAP#1)
where CAP#1 is a fresh type-variable:
CAP#1 extends Object super: Number from capture of ? super Number
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error

Lesen geht, aber der Typ ist Object.

Main.java

public final class Main
{

static void m( java.util.List< ? super java.lang.Number >list )
{ list.add( java.lang.Integer.valueOf( 2 ));
java.lang.System.out.println( list.get( 0 )); }

public static void main( final java.lang.String args[] )
{ final java.util.List< java.lang.Object > list
= new java.util.ArrayList< java.lang.Object >();
m( list ); }}

Aussprachehinweise
super ˈsupɚ (sd)

Ohne Wildcards (invariant)

Schreiben und Lesen ist erlaubt und ergibt genau den richtigen Typ, aber Typargument darf weder Unter- noch Oberklasse sein.

Main.java

public final class Main
{

static void m( java.util.List< java.lang.Number >list )
{ list.add( java.lang.Integer.valueOf( 2 ));
list.add( java.lang.Double.valueOf( 2 )); }

public static void main( final java.lang.String args[] )
{ final java.util.List< java.lang.Number > list
= new java.util.ArrayList<>();
m( list );
java.lang.System.out.println( list.get( 0 ));
java.lang.System.out.println( list.get( 1 )); }}

transcript
2
2.0

Unterklasse geht nicht.

Main.java

public final class Main
{

static void m( java.util.List< java.lang.Number >list )
{ list.add( java.lang.Integer.valueOf( 2 ));
list.add( java.lang.Double.valueOf( 2 )); }

public static void main( final java.lang.String args[] )
{ final java.util.List< java.lang.Integer > list
= new java.util.ArrayList<>();
m( list );
java.lang.System.out.println( list.get( 0 ));
java.lang.System.out.println( list.get( 1 )); }}

transcript
Main.java:11: error: incompatible types: List<Integer> cannot be converted to List<Number>
m( list );
^
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error

Oberklasse geht nicht.

Main.java

public final class Main
{

static void m( java.util.List< java.lang.Number >list )
{ list.add( java.lang.Integer.valueOf( 2 ));
list.add( java.lang.Double.valueOf( 2 )); }

public static void main( final java.lang.String args[] )
{ final java.util.List< java.lang.Object > list
= new java.util.ArrayList<>();
m( list );
java.lang.System.out.println( list.get( 0 ));
java.lang.System.out.println( list.get( 1 )); }}

transcript

Main.java:11: error: incompatible types: List<Object> cannot be converted to List<Number>
m( list );
^
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error

Scheitern einer Instrumentierung (nach Bloch)

Main.java

final class InstrumentedHashSet< E >extends java.util.HashSet< E >
{ private int n = 0; public int getCount(){ return n; }

@java.lang.Override public boolean add( E a ){ ++n; return super.add( a ); };

@java.lang.Override public boolean addAll( java.util.Collection< ? extends E >c )
{ n += c.size(); return super.addAll( c ); }}

public final class Main
{ public static void main( final java.lang.String[] args )
{ final InstrumentedHashSet< java.lang.String >set
= new InstrumentedHashSet< java.lang.String >();

set.addAll( java.util.Arrays.asList( "alpha", "beta", "gamma" ));

java.lang.System.out.println( set.getCount() ); }}

transcript
6

Weiterleitung statt Vererbung

composition + forwarding

it’s not delegation unless the wrapper object passes itself to the wrapped object [Lieberman86; Gamma95, p. 20].” (Bloch )

Main.java

final class InstrumentedHashSet< E >
{ final java.util.HashSet< E >hashSet = new java.util.HashSet<>();

private int n = 0; public int getCount(){ return n; }

public boolean add( E a ){ ++n; return hashSet.add( a ); };

public boolean addAll( java.util.Collection< ? extends E >c )
{ n += c.size(); return hashSet.addAll( c ); }}

public final class Main
{ public static void main( final java.lang.String[] args )
{ final InstrumentedHashSet< java.lang.String >set
= new InstrumentedHashSet< java.lang.String >();

set.addAll( java.util.Arrays.asList( "alpha", "beta", "gamma" ));

java.lang.System.out.println( set.getCount() ); }}

transcript
3

.

.

.

.

Seiteninformationen und Impressum   |   Mitteilungsformular  |   "ram@zedat.fu-berlin.de" (ohne die Anführungszeichen) ist die Netzpostadresse von Stefan Ram.   |   Eine Verbindung zur Stefan-Ram-Startseite befindet sich oben auf dieser Seite hinter dem Text "Stefan Ram".)  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. Schlüsselwörter zu dieser Seite/relevant keywords describing this page: Stefan Ram Berlin slrprd slrprd stefanramberlin spellched stefanram722837 stefan_ram:722837 Typparameter in Java Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd722837, slrprddef722837, PbclevtugFgrsnaEnz Erklärung, Beschreibung, Info, Information, Hinweis,

Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram.
https://www.purl.org/stefan_ram/pub/typparameter_java