Überladung und Signaturen in Java
Den eigentlichen Namen einer Methode (also einen Bezeichner) bezeichnen wir auch als ein Verb (vom lateinischen „verbum“ – „Wort“).
Ein Verb kann zu verschiedenen Methoden derselben Klasse gehören, welche sich durch die Anzahl und/oder den Typ ihrer Parameter unterscheiden.
Die Methode »java.lang.Math.abs« ergibt den Betrag ihres Argumentes (also den Abstand des Arguments zur Zahl 0).
In der Klasse »java.lang.Math« gibt es zwei Methoden mit dem Verb »abs«, aber nur eine mit dem Verb »floor«.
- Dokumentationen zum Verb »abs« der Klasse »java.lang.Math« (verändert und übersetzt)
- Module java.base
Package java.lang
Klasse Math
static double abs( double a )
Ergibt den Betrag des Argumentwertes.
static int abs( int a )
Ergibt den Betrag des Argumentwertes. - Dokumentation zum Verb »floor« der Klasse »java.lang.Math« (verändert und übersetzt)
- Module java.base
Package java.lang
Klasse Math
static double floor( double a )
Ergibt den größten double-Wert, der kleiner als »a« oder gleich »a« ist und bei dem alle Nachkommastellen gleich »0« sind.
Man sagt, daß ein Verb, das mehr als nur einen Eintrag in der Dokumentation eines Referenztyps hat, überladen (d.h. mehrfach mit einer Bedeutungen „beladen“) sei. Und man spricht auch von einer „überladenen Methode “, um zu sagen, daß es noch andere Methoden mit demselben Verb im selben Referenztyp gibt.
Methodensignatur
Um eine Methode eindeutig bezeichnen zu können (wenn ihre Klasse schon festgelegt ist), ist es angesichts der Möglichkeit der Überladung notwendig, zusätzlich zum Verb auch noch die Parametertypen mit anzugeben. Die Kombination aus Verb und den Parametertypen nennt man Methodensignatur oder kurz Signatur. Die obige Dokumentation behandelt also unter anderem die Methodensignatur »abs(int)« und die Methodensignatur »abs(double)«.
- Mögliche Aussprachen von »abs(double)«
- „Abs von double“
„Abs double“ (kurz)
Angabe einer Methode
Da erst die Signatur in eine Methode innerhalb einer Klasse genau bestimmt und nicht schon das Verb der Methode, gibt man Methoden mit ihrer Signatur an.
Man spricht also beispielsweise von der Methode »abs(double)« und nicht einfach von der Methode »abs«.
Wir unterscheiden daher zwischen der Methode »abs(double)« und der Methode »abs(int)« (das sind zwei verschiedene Methoden (Signaturen), die aber dasselbe Verb miteinander teilen).
Man kann jedoch von „der Methode »abs«“ sprechen, wenn es nicht darauf ankommt, welche Überladung genau gemeint ist, oder man sich auf alle Überladungen gemeinsam beziehen will, obwohl der Singular nicht ganz korrekt ist, da es ja mehrere Methoden mit dem Verb »abs« gibt.
Angabe einer Methode ohne Parameter
Man spricht von der Methode »random()« mit leeren Klammern, also der Information, daß sie keine Parameter hat, statt nur von der Methode »random«. Allerdings darf man »random()« in solchen Fällen nicht mit dem Aufruf der Methode verwechseln – bei Methoden ohne Parameter sieht die Angabe der Methodensignatur nämlich wie ein Aufruf der Methode aus. Man muß manchmal aus dem Zusammenhang entnehmen, ob mit solch einer Angabe mit leeren Klammern eine Methodensignatur oder ein Aufruf gemeint ist.
Qualifizierte Signaturen ⃗
Um eine Methode eindeutig zu bezeichnen, wenn nicht schon aus dem Zusammenhang klar ist, zu welcher Klasse sie gehört, ist es nötig, zusätzlich zur Methodensignatur auch noch die Klasse anzugeben, wie beispielsweise in »java.lang.Math.abs(int)«, der Methodensignatur »abs(int)« in der Klasse »java.lang.Math«. Wir bezeichnen eine um den Klassennamen erweiterte Methodensignatur hier manchmal als qualifizierte Methodensignatur.
Aufrufsignatur
Neben der Signatur einer Methode aus der Dokumentation gibt es auch noch die Signatur eines Aufrufs aus dem Quelltext, die Aufrufsignatur.
Die Aufrufsignatur eines Aufrufs erhält man, indem man alle Argumente durch ihren Typ ersetzt.
So hat der Aufruf »abs( 2 )« die Aufrufsignatur »abs(int)«, und der Aufruf »abs( 2.0 )« hat die Aufrufsignatur »abs(double)«.
Das folgende Beispiel zeigt, wie der Compiler eine Aufrufsignatur ausgibt.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( abs( -2.0 )); }}transcript
Main.java:4: error: cannot find symbol
( abs( -2.0 )); }}
^
symbol: method abs(double)
location: class Main
1 error
Auflösung von Ausdrücken
In dem folgenden Programm wird »java.lang.Math.abs« mit einem Argument vom Typ »double« aufgerufen. Ersetzen wir im Aufruf den Argumentausdruck »-2.0« durch seinen Typ »double«, so erhalten wir die Aufrufsignatur »abs(double)«. Diese paßt nur zur Methodensignatur »abs(double)« aus der Dokumentation (in dem Sinne, daß der Aufruf »java.lang.Math.abs( -2.0 )« korrekt wäre, wenn es in der Klasse nur diese Methode »abs(double)« geben würde). Daher wird die Methode aufgerufen, welche die Methodensignatur »abs(double)« hat.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.abs( -2.0 )); }}transcript
2.0
In dem folgenden Programm wird »java.lang.Math.abs« mit einem Argument vom Typ »int« aufgerufen. Ersetzen wir im Aufruf den Argumentausdruck »-2« durch seinen Typ »int«, so erhalten wir die Aufrufsignatur »abs(int)«. Diese paßt sowohl zur Methodensignatur »abs(int)« als auch zur Methodensignatur »abs(double)« aus der Dokumentation, da der Argumenttyp »int« Untertyp sowohl des Parametertyps »int« als auch des Parametertyps »double« ist (in dem Sinne, daß der Aufruf »java.lang.Math.abs( -2 )« sowohl korrekt wäre, wenn es in der Klasse die Methode »abs(int)« geben würde, als auch, wenn es in der Klasse nur die Methode »abs(double)« geben würde.). In solche Fällen wird unter den passenden Signaturen die Methodensignatur mit dem speziellsten Typ ausgewählt. Dies ist im folgenden Programm die Signatur »abs(int)«, da der Typ »int« als Untertyp des Typs »double« spezieller ist.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.abs( -2 )); }}transcript
2
Im allgemeinen wird die Methode aufgerufen, deren Signatur den speziellsten zur Aufrufsignatur passenden Typ hat. Ein Untertyp ist spezieller als der Typ, dessen Untertyp er ist. Daher ist »int« spezieller als »double«
Es gilt als Fehler, wenn es mehrere Methodensignaturen gibt, die zu einer Aufrufsignatur passen und gleich-speziell sind, weil dann nicht entschieden werden kann, welche von ihnen aufgerufen werden soll.
Genauso gilt es natürlich auch als Fehler, wenn es in dem angegebenem Referenztyp (hier beispielsweise »java.lang.Math«) gar keine passende Methodensignatur gibt, beispielsweise beim Aufruf »java.lang.Math.val( -2 )«.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.val( -2 )); }}- Protokoll
Main.java:4: error: cannot find symbol
( java.lang.Math.val( -2 )); }}
^
symbol: method val(int)
location: class Math
1 error
Gründe für eine Überladung
Der Typ des Aufrufs verschiedener Methoden mit demselben Verb muß nicht gleich sein, sondern kann für jede Signatur durch die Dokumentation willkürlich festgelegt werden. Im Falle des qualifizierten Verbs »java.lang.Math.abs« hat der Aufruf der Methode mit einem double-Parameter den Typ »double«, während der Aufruf der Methode mit einem int-Parameter den Typ »int« hat. Dies bedeutet, daß in diesem Falle der Typ des Arguments auch der Typ des Aufrufs ist. Durch die Überladung bleibt in diesem Fall also der Typ des Arguments „erhalten“, was manchmal erwünscht ist.
Bei der Methode »java.lang.Math.floor« geht der Typ des Arguments hingegen verloren. Der Aufruf hat immer den Typ »double«.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.floor( 2.0 )); }}transcript
2.0
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( java.lang.Math.floor( 2 )); }}transcript
2.0
- Tabelle
Aufrufsignatur aufgerufene Methode Aufruftyp
abs(double) abs(double) double
abs(int) abs(int) intfloor(double) floor(double) double
floor(int) floor(double) double
Eine Überladung eines Verbs mit einer int- und einer double-Signatur wird vorgenommen, wenn das Verb int-Werte anders als double-Werte behandeln soll. Würde es nur eine einzige double-Methode geben, so würde diese Methode es nämlich gar nicht feststellen können, wenn sie mit einem int-Werte aufgerufen wird, da dieser beim Aufruf immer automatisch in einen double-Wert umgewandelt werden würde bevor er der Methode bekannt gegeben wird.
Polymorphie
Polymorphie bedeutet, daß die Implementation eines eines Operators oder eines Methodennamens durch jenen Operator beziehungsweise Methodennamen und die Typen der Operanden beziehungsweise Argumente bestimmt wird.
Die Überladung ist eine Form von Polymorphie, da aus der Kombination eines Methodennamens und des Typs der Argumente die zu verwendende Methode bestimmt wird.
Beispielsweise enthält der Aufruf »java.lang.Math.abs( -2.0 )« den Methodennamen »java.lang.Math.abs« und das Argument »-2.0«. Durch die Kombination des Methodennamens mit dem Typ des Arguments, also durch die Aufrufsignatur »java.lang.Math.abs(double)« wird dann festgelegt, daß die Implementation (Methode) »java.lang.Math.abs(double)« aufgerufen wird.
Operationen und Komplemente
Methoden sind Operationen und die Argumentwerte deren Komplemente.
- Allgemeine Begriffe
.--------------------------------------.
| .----------------. .----------. | Maschine aus Operation und Komplement
| | Operation |<---( Komplement ) |
| '----------------' '----------' |
'--------------------------------------'
|
V
\ /
- Interaktion -
/ \
|
VErgebnis
- Methoden und Argumentwerte
.-----------------------------------------.
| .----------------. .-------------. | Maschine aus Methode und Argumentwerten
| | Methode |<---( Argumentwerte ) |
| '----------------' '-------------' |
'-----------------------------------------'
|
V
\ /
- Berechnung -
/ \
|
VErgebnis
- Konkretes Beispiel
.-----------------------------------------.
| .----------------. .-------------. | Maschine aus abs(int),
| | abs(int) |<---( 5 ) | eine konkrete Inkarnation von abs(int)
| '----------------' '-------------' |
'-----------------------------------------'
|
V
\ /
- Berechnung -
/ \
|
V5
Teilausdrücke
Als eine Verallgemeinerung des Begriffs Operand und Argument führen wir den Begriff Teilausdruck ein.
Definition Ein Teilausdruck eines Ausdrucks ist jeder andere Ausdruck, der im ersten Ausdruck als Operand oder als Argument vorkommt.
Beispielsweise hat der Ausdruck »2 + java.lang.Math.negateExact( 3 )« die Teilausdrücke »2«, »java.lang.Math.negateExact( 3 )« und »3«.
Beispiel Wandlung von Zahlen nach »java.lang.String« ⃗
Im nächsten Beispiel wird der ganzzahlige Wert von »1« in eine Zeichenfolge gewandelt und dann hinter das Zeichen »1« geschrieben.
Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 1 + java.lang.String.valueOf( 1 )); }}transcript
11
Im nächsten Beispiel wird der double-Wert »1.0« in eine Zeichenfolge gewandelt und dann hinter das Zeichen »1« geschrieben.
Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 1 + java.lang.String.valueOf( 1.0 )); }}transcript
11.0
Wir sehen hier, daß die Methode »java.lang.String.valueOf« für verschiedene Argumenttypen überschrieben wurde. Anders als bei »java.lang.Math.abs« ist der Typ des Aufrufs hier aber nun nicht der Argumenttyp, sondern immer »java.lang.String«.
Der Unterschied der beiden Methoden »java.lang.String.valueOf(int)« und »java.lang.String.valueOf(double)« besteht hier darin, daß die zweiten die Zahl immer mit einem Punkt darstellt. Die Überladung erlaubt es hier also, den int-Wert »1« anders darzustellen als den den double-Wert »1.0«. Auch hier erfolgt die Überladung also, weil ein int-Argument anders als eine double-Argument behandelt werden soll. Ohne Überladung würde die Methode es gar nicht bemerken, wenn sie mit einem int-Argument aufgerufen wird.
Die Umwandlung wäre auch durch eine Zeichenfolgenverkettung möglich, wie das folgende Beispiel zeigt.
Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 1 + ( "" + 1 )); }}- Protokoll
11
Übungsfragen
? Überladungen
Wie viele Methoden hat das Verb »getInteger« in der Klasse »java.lang.Integer«?
? Kugelvolumen
Das folgende Java -Programm soll das Volumen einer Kugel mit einem Radius von 3 Metern berechnen. Das Volumen ‹ V › einer Kugel mit Radius ‹ r › beträgt ‹ 4/3 π r ³ ›. Wo steckt der Fehler in dem Programm? (Das in der nächsten Zeile verwendete mathematische Symbol ‹ ∧ › bedeutet „und“, das Symbol ‹ ⇒ › hat eine niedrigere Priorität als ‹ ∧ › und bedeutet „daraus folgt“.)
- ( V = 4/3 π r ³ )∧( r = 3 ) ⇒ V = 4/3 π r ³ ⇒ V = 4/3 π · 3³ = 4 π · 3² = 36 π ≈ 113,09733552923255658465516179806
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 4/3 * java.lang.Math.PI * java.lang.Math.pow( 3, 3 )); }}Protokoll
84.82300164692441
In einer Planung für die Marinersonde Mariner 1 fand sich »ʀ̅̇« für den Mittelwert der Ableitung einer Größe »ʀ«. Beim handschriftlichen Übertragen in die Spezifikation wurde der Strich vergessen und nur »ʀ̇« geschrieben. Dies führte letztendlich zum Absturz der Rakete am 22. Juli 1962.
? Aufrufsignaturen _
Welche Aufrufsignatur hat der Aufruf »sin( 2.0 )«?
Welche Aufrufsignatur hat der Aufruf »sin( 2 )«?
? Methoden und Signaturen _
Welche Methodensignatur hat die Methode »log« der Klasse »java.lang.Math«?
? Aufrufe und Signaturen ⃗
Was trifft für »abs( 2 )« zu?
- Es ist ein Aufruf.
- Es ist eine Signatur.
- Es kann beides sein.
? Aufrufe und Signaturen (1) ⃗
Was trifft für »abs(int)« zu?
- Es ist ein Aufruf.
- Es ist eine Signatur.
- Es kann beides sein.
? Aufrufe und Signaturen (2) ⃗
Was trifft für »random()« zu?
- Es ist ein Aufruf.
- Es ist eine Signatur.
- Es kann beides sein.
? Aufrufe und Signaturen (3) ⃗
- / Welche der folgenden Aufrufe haben die gleiche Signatur?
- »f( 23 )«
- »f( 12 )«
- »g( 2. )«
- »f( 2. )«
? Datentypen ⃗
Welchen Datentyp haben die folgenden Aufrufe jeweils?
- »java.lang.Math.max( 2, 3 )«
- »java.lang.Math.max( 2.0, 3.0 )«
- »java.lang.Math.max( 2, 3.0 )«
? Datentypen (1) ⃗
Welchen Datentyp haben die folgenden Aufrufe jeweils?
- »java.lang.System.getProperty( "2" )«
- »java.lang.System.getProperty( "2", "3" )«
- Aussprachehinweis
- property ˈprɑp ɚ ti
Diskussion der Typwandung mit Hilfe von »+« ⃗
Wir schon früher behandelt wurde, wird gelegentlich die Wandlung in eine Zeichenfolge mit »+ ""« als „unverständlich“ kritisiert. Die Kritiker würden die Verwendung von »java.lang.String.valueOf« bevorzugen, vermutlich weil dort Englische Wörter vorkommen, die – mehr oder weniger – „erklären“, was gemeint ist, während »+ ""« keine solchen Wörter enthält. Wenn Texte besser lesbar wären als Operatorzeichen, müßten jene Kritiker dann aber eigentlich auch die Funktion »java.lang.Double.sum« zur Berechnung einer Summe verwenden.
? Ausgabe vorhersagen ⃗
Das folgende Programm gibt elf Zeichen aus. Sagen Sie alle elf Zeichen einzeln voraus!
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 2E3 + ", " + java.lang.Math.pow( 2, 3 )); }}
Übungsaufgaben
/ Übersetzung
Schreiben Sie den Ausdruck »2 + 3*4« so um, daß zur Addition die Methode »java.lang.Math.addExact« und zur Multiplikation die Methode »java.lang.Math.multiplyExact« verwendet wird.
/ Übungsaufgabe ⃗
Schreiben Sie ein Programm, in dem zunächst die Methode »max(double,double)« aus der Klasse »java.lang.Math« und dann die Methode »max(int,int)« aus derselben Klasse aufgerufen wird.
Beispiele Hin- und Herwandlungen ⃗
Verben, die eine Zahl in eine Zeichenfolge wandeln, und Argumente vom Typ »double« und vom Typ »int« akzeptieren können sollen, können nicht einfach nur eine Methode mit einem double-Parameter haben, wenn sie int-Werte durch das Fehlen eines Punkts in ihrem Ergebnis kennzeichnen sollen, denn bei einem double-Parameter würden auch alle int-Werte in double-Werte gewandelt werden, und die Methode würde es gar nicht „erfahren“, wenn sie mit einem int-Argument aufgerufen wird (sie sieht dann immer nur double-Werte als Argumentwerte). Statt dessen muß eine weitere, gleichnamige Methode mit einem int-Parameter deklariert werden, die bei einem Aufruf des Verbes mit einem int-Argument aktiv wird. Diese Methode „weiß“ dann, daß sie nicht mit einem double-Argument aufgerufen wurde und kann den Punkt ».« entsprechend weglassen.
Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 1 + java.lang.String.valueOf( 2 + java.lang.Double.parseDouble( "4" ))); }}Protokoll
16.0
Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 1 + java.lang.String.valueOf( 2 + java.lang.Integer.parseInt( "4" ))); }}transcript
16
Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 1 + java.lang.Double.parseDouble( 2 + java.lang.String.valueOf( 4 ))); }}Protokoll
25.0
Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 1 + java.lang.Integer.parseInt( 2 + java.lang.String.valueOf( 4 ))); }}transcript
25
Zitate *
- Java 2017 * (gekürzt)
- JLS9: 8.4.2 Method Signature
- Two methods or constructors, M and N, have the same signature if they have the same name and the same formal parameter types.
- Java 2017 * (gekürzt; als Indiz dafür, daß nur der eigentliche Methodenname (das Verb) Teil der Signatur ist, der letzte Punkt würde keinen Sinn ergeben, wenn die Signatur den qualifizierten Namen beinhalten würde.)
- JLS9: 8.4.8 Inheritance, Overriding, and Hiding A class C inherits from its direct superclass all concrete methods m (both static and instance) of the superclass for which all of the following are true: • m is a member of the direct superclass of C. • m is public, protected, or declared with package access in the same package as C. • No method declared in C has a signature that is a subsignature of the signature of m.
- Aussprachehinweise
- superclass ˈsupɚ ˈklæs
- Java 2017 * (Erklärung von “subsignature”, gekürzt)
- The signature of a method m1 is a subsignature of the signature of a method m2 if either: • m2 has the same signature as m1, or • the signature of m1 is the same as the erasure of the signature of m2.
- Java 2015 *
- 13.1 The Form of a Binary
…
- 5.
…
- The signature of a method must include all of the following as determined by §15.12.3:
- • The simple name of the method
- • The number of parameters to the method
- • A symbolic reference to the type of each parameter
- Java 2015 *
- 8.4.9 Overloading
- If two methods of a class (whether both declared in the same class, or both inherited by a class, or one declared and one inherited) have the same name but signatures that are not override-equivalent, then the method name is said to be overloaded.
- Zitat aus der Sprachspezifikation zur Überladung *
- “This step uses the name of the method and the types of the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.
- There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is one used at run-time to perform the method dispatch.”
- JLS7 15.12.2 Compile-Time Step 2: Determine Method Signature
- Bibelzitat als Beispiel zu "verbum"
- In principio erat Verbum, et Verbum erat apud Deum, et Deus erat Verbum.