Die Substitutionsregel in Java (Die Substitutionsregel in Java), Lektion, Seite 723213
https://www.purl.org/stefan_ram/pub/substitutionsregel_java (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram
Java-Kurs

Das Substitutionsprinzip in Java 

Substitutionsprinzip Wird ein Ausdruck eines Typs erwartet, so kann ein Ausdruck eines Untertyps  angegeben werden.

Wir hatte dieses Prinzip bereits im Grundkurs kennenlernt. Dort wurde behandelt: Der Datentyp »int« ist ein Untertyp des Datentyps »double«, da jeder int-Wert auch als double-Wert dargestellt werden kann. Deswegen kann ein int-Wert überall verwendet werden, wo auch ein double-Wert verwendet werden kann.

Allgemein muß beim Kopieren eines Wertes der Typ des Ausdrucks, der den Wert angibt, eine Untertyp  des Typs des Ausdrucks, der das Ziel des Kopierens angibt, sein. Als Spezialfälle dieses Prinzips ergeben sich:

Zitat
Substitution Principle: a variable of a given type may be assigned a value of any subtype of that type, and a method with a parameter of a given type may be invoked with an argument of any subtype of that type.
Java Generics and Collections Maurice Naftalin  & Philip Wadler

Substitutionsprinzip und Einträge

Ein Untertyp erweitert  einen Typ oft um weitere Einträge. Da man diese zusätzlichen Einträge aber auch einfach ignorieren  kann, ist, wenn ein Ausdruck eines bestimmten Typs erwartet wird, auch ein Ausdruck eines Untertyp akzeptabel.

Eine Typerwartung sagt eigentlich „Ich brauche alle Methoden dieses Typs.“, aber diese Methoden hat man auch bei allen Untertypen.

Der Typ X übernimmt drei Methoden aus einem direkten Obertyp und fügt noch zwei eigene hinzu
           Typ O, direkter Obertyp
| |
| f() g() h() |
| |
-----|-----------------|-------------
Typ X | f() g() h() | i() j()
-------------------------------------

Wenn ein Ausdruck eines Typs O erwartet wird, so liegt dies normalerweise daran, daß die Methoden dieses Typs (wie zum Beispiel f(), g() und h()) benötigt werden. Ein Untertyp, wie der Typ X, enthält aber auch diese Methoden und ist daher ebenfalls akzeptabel.

Beispielsweise ist ein Luxusauto, das um ein Radio und eine Klimaanlage erweitert wurde, weiterhin eine Auto (es kann als Auto verwendet werden).

Ein Luxusauto übernimmt alle Aspekte eines Autos und fügt noch etwas hinzu
                            Auto
| |
| Gas Bremse Kupplung |
| |
-----|-------------------------|-----------------------
Luxusauto | Gas Bremse Kupplung | Radio Klimaanlage
-----------------------------------------------------

Methoden und Referenztypen

Die Methode »println« eines Ausdrucks vom Typ »java.lang.System.out« hat beispielsweise einen Parameter vom Typ »java.lang.Object«, aber nicht vom Typ »java.io.PrintStream«. Sie kann aber mit dem Ausdruck »java.lang.System.out« als Argument aufgerufen werden, weil der Typ dieses Ausdrucks, also »java.io.PrintStream«, ein Untertyp des Parametertyps »java.lang.Object« ist.

Dokumentation (übersetzt und verändert)

java.io

Class PrintStream

void println( java.lang.Object x )

Gibt das als Argumentwert übergebene Objekt und ein Zeilenende aus.
Substitution eines Untertyps
java.lang.System.out.println( java.lang.Object )
^
| ^ ist Untertyp von
|
java.io.PrintStream
^
| ^ ist Ausdruck mit Typ
|
java.lang.System.out
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.System.out ); }}
transcript
java.io.PrintStream@15db9742

Ein Parameter vom Typ »java.lang.Object« drückt aus, daß an dieser Ausdrücke benötigt werden, die – als Kontexte verwendet – Aufrufe aller Methoden der Klasse »java.lang.Object« erlauben.

Dokumentation (übersetzt und verändert)

javax.lang.model

Enum SourceVersion

static boolean isIdentifier( java.lang.CharSequence name )

Ergibt, ob der als Argumentwert angegebene Name ein Bezeichner ist.

Die static-boolean-Methode »javax.lang.model.SourceVersion.isIdentifier(java.lang.CharSequence)« kann mit einem Argument vom Typ »java.lang.String« aufgerufen werden, obwohl ihr Parameter den Typ »java.lang.CharSequence« hat, weil »java.lang.String« ein Untertyp von »java.lang.CharSequence« ist.

Substitution eines Untertyps
javax.lang.model.SourceVersion.isIdentifier( java.lang.CharSequence )
^
| ^ ist Untertyp von
|
java.lang.String
^
| ^ ist Ausdruck von
|
"args"
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( javax.lang.model.SourceVersion.isIdentifier( "args" )); }}
transcript
true

»java.lang.CharSequence«

Als Beispiel einer Beziehung zwischen drei Typen zeigen wir hier die Typen »java.lang.Object«, »java.lang.CharSequence« und »java.lang.String«. Dabei stehen Obertypen eines Typs links von diesem Typ. Man erkennt deutlich, daß Untertypen mehr Methoden enthalten können als ihre Obertypen. Ein „String“ ist sozusagen eine „Luxus-CharSequence“.

Der Typ »java.lang.String« übernimmt Methoden aus dem Obertyp »java.lang.CharSequence« und fügt noch eigene hinzu
.------------------------------.-------------------------------.------------------------------------------------------------.
| java.lang.Object | java.lang.CharSequence | java.lang.String |
|==============================|===============================|============================================================|
| equals(java.lang.Object) | equals(java.lang.Object) | equals(java.lang.Object) |
| getClass() | getClass() | getClass() |
| hashCode() | hashCode() | hashCode() |
| toString() | toString() | toString() |
|------------------------------|-------------------------------|------------------------------------------------------------|
| | length() | length() |
| | charAt(int) | charAt(int) |
| | subSequence(int,int) | subSequence(int,int) |
| | chars() | chars() |
| | codePoints() | codePoints() |
| |-------------------------------|------------------------------------------------------------|
| | | isEmpty() |
| | | codePointAt(int) |
| | | codePointBefore(int) |
| | | codePointCount(int,int) |
| | | offsetByCodePoints(int,int) |
| | | getChars(int,int,char[],int) |
| | | getBytes(int,int,byte[],int) |
| | | getBytes(java.nio.charset.Charset) |
| | | getBytes() |
| | | equals(java.lang.Object) |
| | | contentEquals(java.lang.StringBuffer) |
| | | contentEquals(java.lang.CharSequence) |
| | | equalsIgnoreCase(java.lang.String) |
| | | compareTo(java.lang.String) |
| | | compareToIgnoreCase(java.lang.String) |
| | | regionMatches(int,java.lang.String,int,int) |
| | | regionMatches(boolean,int,java.lang.String,int,int) |
| | | startsWith(java.lang.String,int) |
| | | startsWith(java.lang.String) |
| | | endsWith(java.lang.String) |
| | | hashCode() |
| | | indexOf(int) |
| | | indexOf(int,int) |
| | | lastIndexOf(int) |
| | | lastIndexOf(int,int) |
| | | indexOf(java.lang.String) |
| | | indexOf(java.lang.String,int) |
| | | lastIndexOf(java.lang.String) |
| | | lastIndexOf(java.lang.String,int) |
| | | substring(int) |
| | | substring(int,int) |
| | | concat(java.lang.String) |
| | | replace(char,char) |
| | | matches(java.lang.String) |
| | | contains(java.lang.CharSequence) |
| | | replaceFirst(java.lang.String,java.lang.String) |
| | | replaceAll(java.lang.String,java.lang.String) |
| | | replace(java.lang.CharSequence,java.lang.CharSequence) |
| | | split(java.lang.String,int) |
| | | split(java.lang.String) |
| | | toLowerCase(java.util.Locale) |
| | | toLowerCase() |
| | | toUpperCase(java.util.Locale) |
| | | toUpperCase() |
| | | trim() |
| | | toCharArray() |
| | | java.lang.String valueOf(java.lang.Object) |
| | | java.lang.String valueOf(char[]) |
| | | java.lang.String valueOf(char[],int,int) |
| | | java.lang.String copyValueOf(char[],int,int) |
| | | java.lang.String copyValueOf(char[]) |
| | | java.lang.String valueOf(boolean) |
| | | java.lang.String valueOf(char) |
| | | java.lang.String valueOf(int) |
| | | java.lang.String valueOf(long) |
| | | java.lang.String valueOf(float) |
| | | java.lang.String valueOf(double) |
| | | java.lang.String intern() |
| | | compareTo(java.lang.Object) |
'------------------------------'-------------------------------'------------------------------------------------------------'

Obertypentest durch Zuweisung (Initialisierung) ℳ

Lesehinweis In diesem Kurs sind Teile, die Kenntnisse über Methodendeklarationen voraussetzen, teilweise mit „ ℳ “ gekennzeichnet. Leser, die noch keine Kenntnisse über Methodendeklarationen haben, können diese Kursteile einfach überspringen. Es reicht aber vielleicht auch, wenn man sich merkt, daß eine Methodendeklaration mit »public static« beginnt und mit »}« endet (vereinfacht gesagt) und in den runden Klammern, die auf »public static« folgen, Variablen deklariert werden können, die man „Parameter“ nennt.

Die folgende Methode wird von Compiler akzeptiert, weil »java.lang.CharSequence« ein Obertyp von »java.lang.String« ist.

Main.java

public final class Main
{

public static void test( final java.lang.String string )
{ final java.lang.CharSequence sequence = string; }

public static void main( final java.lang.String[] args )
{ }}

transcript
(keine Ausgabe)

Die folgende Methode wird von Compiler akzeptiert, weil »java.lang.CharSequence« ein Obertyp von »java.lang.String« ist.

Das folgende Programmbeispiel zeigt, daß »java.lang.String« kein Untertyp von »java.lang.CharSequence« ist.

Main.java

public final class Main
{

public static void test( final java.lang.CharSequence sequence )
{ final java.lang.String string = sequence; }

public static void main( final java.lang.String[] args )
{ }}

transcript
Main.java:5: error: incompatible types: CharSequence cannot be converted to String
{ final java.lang.String string = sequence; }
^
1 error

Mit »cannot be converted to« sagt der Compiler, daß der rechts stehende Typ kein Untertyp  des linksstehenden Typs ist.

Obertypentest durch Aufruf ℳ ⃗

Das folgende Programm wird von Compiler akzeptiert, weil »java.lang.CharSequence« ein Obertyp von »java.lang.String« ist.

Main.java

public final class Main
{

public static void sequence( final java.lang.CharSequence sequence ) {}

public static void test( final java.lang.String string ) { sequence( string ); }

public static void main( final java.lang.String[] args )
{ }}

transcript
(keine Ausgabe)

Das folgende Programmbeispiel zeigt, daß »java.lang.String« kein Untertyp von »java.lang.CharSequence« ist.

Main.java

public final class Main
{

public static void string( final java.lang.String string ) {}

public static void test( final java.lang.CharSequence sequence ) { string( sequence ); }

public static void main( final java.lang.String[] args )
{ }}

transcript
Main.java:5: error: incompatible types: CharSequence cannot be converted to String
{ final java.lang.String string = sequence; }
^
1 error

Mit »cannot be converted to« sagt der Compiler, daß der rechts stehende Typ kein Untertyp  des linksstehenden Typs ist.

Obertypentests ℳ *

Wenn »S « und »T « beide Referenztypen sind, dann wird das folgende Programm vom Compiler ohne Fehlermeldung genau dann akzeptiert wird, wenn »T « ein Obertyp von »S « ist. Auf diese Weise ist es möglich, auch ohne Lesen der Dokumentation zu ermitteln, ob ein Typ ein Obertyp eines anderen Typs ist.

Main.java

public final class Main
{

public static void test( final S  s )
{ final T  t = s; }

public static void main( final java.lang.String[] args )
{ }}

Wenn »S « und »T « beide Referenztypen sind, dann das folgende Programm vom Compiler ohne Fehlermeldung genau dann akzeptiert wird, wenn »T « ein Obertyp von »S « ist. Auf diese Weise ist es möglich, auch ohne Lesen der Dokumentation zu ermitteln, ob ein Typ ein Obertyp eines anderen Typs ist.

Main.java

public final class Main
{

public static void t( final T  t ) {}

public static void test( final S  s ) { t( s ); }

public static void main( final java.lang.String[] args )
{ }}

Übungsfragen

?   Übungsfrage

Welcher Typ kann mehr  Methoden enthalten?

?   Übungsfrage

Welche der folgenden Aussagen treffen zu?

?   Übungsfrage

Welche der folgenden Aussagen treffen zu?

?   Übungsfrage

Welche der folgenden Aussagen treffen zu?

?   Übungsfrage ℳ

Können Sie vorhersagen, ob das folgende Programm vom Compiler akzeptiert werden wird, ohne es auszuprobieren?

Bei Übungsaufgabe dieser Art ist zu beachten, daß die verwendeten Variablen durch die Deklarationen einen Typ  erhalten. In dem folgenden Programm erhält die Variablen »sequence« beispielsweise den Typ »java.lang.CharSequence« und die Variable »string« den Typ »java.lang.String«. Da »sequence« ein Parameter ist, kann es in unserem Programm einen Typ erhalten ohne jemals einen Wert zu erhalten! Bei der Beantwortung der Aufgabe soll dann beurteilt werden, ob ein Aufruf oder eine Zuweisung/Initialisierung (hier: »string = sequence«) bei den gegebenen Typen erlaubt ist.

Main.java

public final class Main
{

public static void test( final java.lang.CharSequence sequence )
{ final java.lang.String string = sequence; }

public static void main( final java.lang.String[] args )
{ }}

?   Übungsfrage (1) ℳ

Können Sie vorhersagen, ob das folgende Programm vom Compiler akzeptiert werden wird, ohne es auszuprobieren?

Main.java

public final class Main
{

public static void test( final java.lang.String string )
{ final java.lang.String string1 = string; }

public static void main( final java.lang.String[] args )
{ }}

?   Übungsfrage (2) ℳ

Können Sie vorhersagen, ob das folgende Programm vom Compiler akzeptiert werden wird, ohne es auszuprobieren?

Main.java

public final class Main
{

public static void test( final java.lang.String string )
{ final java.lang.CharSequence sequence = string; }

public static void main( final java.lang.String[] args )
{ }}

?   Übungsfrage (3) ℳ

Können Sie vorhersagen, ob das folgende Programm vom Compiler akzeptiert werden wird, ohne es auszuprobieren?

Main.java

public final class Main
{

public static void test( final java.lang.String string ) { sequence( string ); }

public static void sequence( final java.lang.CharSequence sequence ) {}

public static void main( final java.lang.String[] args )
{ }}

?   Übungsfrage (4) ℳ

Können Sie vorhersagen, ob das folgende Programm vom Compiler akzeptiert werden wird, ohne es auszuprobieren?

Main.java

public final class Main
{

public static void string( final java.lang.String string ) {}

public static void test( final java.lang.CharSequence sequence )
{ string( sequence ); }

public static void main( final java.lang.String[] args )
{ }}

 Übungsfrage (5) ℳ

Können Sie vorhersagen, ob das folgende Programm vom Compiler akzeptiert werden wird, ohne es auszuprobieren?

Main.java

public final class Main
{

public static void string( final java.lang.String string ) {}

public static void test( final java.io.PrintStream stream ) { string( stream ); }

public static void main( final java.lang.String[] args )
{ }}

Übungsaufgaben ⃗

/   Übungsaufgabe ℳ ⃗

Ermitteln Sie durch Ausprobieren, ob das folgende Programm vom Compiler akzeptiert wird, und damit, ob »java.lang.StringBuilder« ein Untertyp von »java.lang.CharSequence« ist.

Main.java

public final class Main
{

public static void sequence( final java.lang.CharSequence sequence ) {}

public static void test( final java.lang.StringBuilder builder )
{ sequence( builder ); }

public static void main( final java.lang.String[] args )
{ }}

/   Übungsaufgabe (1) ℳ ⃗

Ermitteln Sie durch Ausprobieren, ob das folgende Programm vom Compiler akzeptiert wird, und damit, ob »java.lang.Integer« ein Untertyp von »java.lang.Double« ist.

Main.java

public final class Main
{

public static void d( final java.lang.Double d ) {}

public static void test( final java.lang.Integer i ) { d( i ); }

public static void main( final java.lang.String[] args )
{ }}

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 stefanram723213 stefan_ram:723213 Die Substitutionsregel in Java Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd723213, slrprddef723213, 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/substitutionsregel_java