Die Grundidee von Exceptions ist es einen Fehlerkontext
aufzuspannen, der erst näher betrachtet werden muß, wenn
man Fehler korrigieren will. Der Programmcode sollte durch die
Behandlung von Fehlern nicht undurchsichtig und unleserlich werden.
Meiner Meinung nach sollten Exceptions deswegen auch nicht als
zweiter Returnwert vergewaltigt werden.
Ziel: Nach dem
Auftreten einer Exception sollte es möglichst einfach sein das
System in einen definierten arbeitsfähigen Zustand zu
versetzen.
Es gibt grundsätzlich drei verschiedenen Arten von
Exceptions. Diese unterscheiden sich durch den Zustand in dem sie das
Objekt hinterlassen.
Die von der Methode benutzten Objekte sind in einem undefinierten Zustand. Jede auf dem Objekt aufgerufene Methode muß nach einer solchen Exception nicht mehr ihre Spezifikation einhalten. Diese Exception wird im folgenden mit Undefined Exception benannt. Dabei ist zu beachten, daß keine weiteren Resourcen, außer die angegebenen, benutzt werden. Außerdem werden ReadOnly Resourcen nicht modifiziert.
Die von der Methode benutzten Objekte sind in einem genau definierten Zustand, der aber von der Zusicherung der Methode abweicht. Diese Exception wird im folgenden mit Defined Exception benannt. Dabei ist zu beachten, daß keine weiteren Resourcen, außer die angegebenen, benutzt werden. Außerdem werden ReadOnly Resourcen nicht modifiziert.
Die von der Methode benutzten Objekte sind in ihrem vorherigen Zustand, der aber von der Zusicherung der Methode abweicht. Diese Exception wird im folgenden mit Transacted Exception benannt. Dabei ist zu beachten, daß keine weiteren Resourcen, außer die angegebenen, benutzt werden. Außerdem werden ReadOnly Resourcen nicht modifiziert. Diese Spezifikation trifft auch auf Defined Exception zu, wegen ihrer Bedeutung führe ich sie extra auf.
Die Benutzbarkeit eines Objektes, nachdem eine Exception aufgetreten ist, ist vom obigen Typ der Exception abhängig.
Satz 1.1: Nachdem eine Undefined Exception aufgetreten ist, kann mit dem Objekt sowie allen ReadWrite Resourcen nicht mehr weiter gearbeitet werden.
Satz 1.2: Nachdem eine Defined Exception aufgetreten ist, kann aufgrund des genau definierten Zustandes weiter gearbeitet werden.
Satz 1.3: Nach einer Transacted Exception ist der gleiche Zustand wie vor dem Aufruf wiederhergestellt.
Es sollten möglichst nur Transacted Exception ausgelöst werden. Bei komplizierten Methoden läßt sich aber eine Defined Exception nicht immer vermeiden. Eine Undefined Exception deutet immer auf eine Programmierfehler hin. Der Typ der Exeption kann nur in Zusammenhang mit der Methode in der sie Auftritt ermittelt werden.
Satz 1.4: Durch die Klasse der Exception kann niemals alleine der Typ (undefined, defined oder transacted) entschieden werden.
Die Grundidee von Resourcen ist die Aufteilung eines Gebildes in
weitere Einheiten. Auf diesen können dann verschiedene Aufträge
gleichzeitig arbeiten, wenn sie nicht dieselben Resourcen benutzen.
Z.B. kann man in einer Textverarbeitung die einzelnen Dokumente als
Einheiten betrachten. Aufträge, die sich nur auf ein Dokument
beziehen, können parallel zu anderen Dokumenten bearbeitet
werden.
Mit Resourcen sind im System bzw. der Applikation
vorhandene Objekte, Services, Kanäle ... gemeint, die zur Zeit
nur von einem Thread benutzt werden können. Als Konsequenz
müssen Resourcen einem Thread zugeordnet werden, bevor dieser
sie benutzt.
Ziel: Es muß möglich sein, 1.
Aufträge parallel abzuarbeiten, 2. die Frage Warum können
zwei Aufträge nicht parallel arbeiten? beantwortet zu
können.
Es gibt verschiedene Möglichkeiten diese
Zuordnung vorzunehmen. Zwei stelle ich kurz vor.
Eine Art der Zuordnung ist das vorherige Anfordern aller für den Auftrag benötigten Resourcen. Ist dies möglich, kann der Auftrag ohne weitere Störungen ablaufen. Die Resourcen dürfen freigegeben werden, bevor der Auftrag beendet ist. Dies gilt natürlich nur für nicht mehr verwendete Resourcen. Es darf ebenfalls das Zuordnungsrecht von lesend und schreibend auf lesend zurückgenommen werden. Diese Zuornungsart wird im weiteren mit Prealloc Resource Konzept bezeichnet.
Eine andere Art der Zuordnung ist das Anfordern der Resourcen, wenn sie benötigt werden. Dabei kann es zu Störungen kommen, wenn sich verschiedene Aufträge um die gleiche Resource bewerben. Die Resourcen dürfen freigegeben werden, bevor der Auftrag beendet ist. Dies gilt natürlich nur für nicht mehr verwendete Resourcen. Es darf ebenfalls das Zuordnungsrecht von lesend und schreibend auf lesend zurückgenommen werden. Diese Zuornungsart wird im weiteren mit Ondemand Resource Konzept bezeichnet.
Es gibt noch weitere Möglichkeiten Aufträge zu
bearbeiten, die die gleichen Resourcen benutzen. Häufig findet
man solche Lösungen bei Datenbankanwendungen.
In der
folgenden Tabelle stehen sich die beiden Konzepte mit ihren Vor- und
Nachteilen und ihren Anforderungen gegenüber.
|
|
|
---|---|---|
Alle Resourcen müssen vor der Auftragsausführung bekannt sein. |
Ja |
Nein |
Nicht mehr benötigte Resourcen dürfen freigegeben werden. |
Ja |
Ja |
Es kann zu Verklemmungen oder Races kommen. |
Nein |
Ja |
In Bearbeitung befindliche Aufträge müssen, aufgrund fehlender Resourcen, abgebrochen werden. |
Nein |
Ja |
Der Zustand der Resourcen ist zu jedem Zeitpunkt der Auftragsabarbeitung bekannt. |
Ja |
Nein |
Algorithmus zur Resourcevergabe. |
Einfach, da nur überprüft werden muß, ob alle benötigten Resourcen verfügbar sind. |
Komplex, da neben dem Anfordern von Resourcen auch noch überprüft werden muß, ob das System lebendig ist. |
Parallelität |
Hoch, da unabhängige Aufträge meistens nur lesend auf gemeinsame Resourcen zugreifen. |
Sehr hoch, da die benötigten Resourcen erst angefordert werden, wenn man sie braucht. |
Meiner Meinung nach ist nur das Prealloc Resource Konzept ohne richtige Programmierwerkzeuge zur Entwicklung paralleler Algorithmen (z.B. Netzprogrammierung) wenigstens ein bißchen beherschbar.
Es stellt sich die Frage wie das Prealloc
Resource Konzept in einem Komponenten-Modell und in einem
Objekt-Environment integriert werden kann. Ein Objekt-Environment ist
ein mehr oder weniger dynamische Menge von Objekten die miteinander
in Verbindung stehen. Aus dem obigen Beispiel könnte man die
Verbindung der Textverarbeitung zu ihren Dokumenten als
Objekt-Environment bezeichnen. Ein Objekt in diesem Environment kann
nun über seine Verbindungen mit anderen Objekten kommunizieren.
Die Schnittstellen mit denen über die Verbindung kommuniziert
wird nennt man Komponenten-Modell. Die Idee des Objekt-Environments
ist es weitere Objekte möglichst einfach zu integrieren. So
könnten in unserem Beispiel weitere Dokumenttypen wie ein
HTML-Dokument eingebunden werden. Die Schittstellen müßten
nur dem, von der Textverarbeitung geforderten, Komponenten-Modell
genügen. Liefert aber das Modell, wie heute üblich, keine
Information über die benötigten Resourcen bei Benutzung der
Schnittstellen, dann können Verklemmungen bzw. Inkonsistenzen
nicht vermieden werden. Aus diesem Grunde ist es notwendig, das
Resource-Konzept in das Komponenten-Modell zu integrieren.
Ziel:
Es muß ein Kompromiß zwischen hoher Nebenläufigkeit
und der damit verbundenen Komplexität, sowie einfacher
Programmierung und geringer Nebenläufigkeit gefunden
werden.
Folgen: In einem Objekt-Environment müssen die
einzelnen Objekte also dynamisch auf Verbindungen zu Objekten mit
hoher oder geringer Nebenläufigkeit reagieren. Die Komplexität
dieser Aufgabe darf aber nicht in die Objekte verlagert werden, da
von einem seriellen Objekt (bzw. dessen Programmierer) nicht die
Steuerung nebenläufiger Aufträge verlangt werden
kann.
Lösungsansatz: Die Behandlung der
Nebenläufigkeit wird nicht in einer einfachen Komponente
implementiert. Das bedeutet sie muß mit einer
Default-Behandlung zufrieden sein, die minimale Nebeläufigkeit
nach sich zieht. Eine Komponente kann sich aber in die Vergabe der
Resourcen einmischen. So kann sie ihren Grad der Nebenläufigkeit
erhöhen. Dies ist dann aber auch mit erhöhtem
Implementationsaufwand zu bezahlen. Auf der anderen Seite macht es
aber keinen Sinn serielle oder Komponenten mit zu großem
Resourcebedarf einzubinden, wenn das Objekt-Environment danach
praktisch nicht mehr lauffähig ist. Das bedeutet, daß das
Objekt-Environment auch Forderungen bezüglich des Resourcebedarf
an die Komponenten stellen darf.
Es muß ein Modell geben, in dem alle vorhandenen Resourcen und deren Beziehung zueinander eingetragen werden. Dadurch kann abgeschätzt werden, welchen Resourcebedarf eine Komponente hat. Das Schätzen ist wörtlich zu nehmen. (Im Zusammenhang mit Security wird man aber auch noch sehen, daß der Zugriff auf bestimmte Resourcen nicht möglich ist.) Für das Prealloc Resource Konzept gilt, es müssen mindestens die benötigten Resourcen verfügbar sein. Zur Not sind diese alle.
Eine nebenläufige Komponente muß in jeder ihrer von außen erreichbaren Methoden kontrollieren, ob die entsprechenden Resourcen für sie angefordert wurden. Damit serielle Komponenten diese Methoden nutzen können, können die benötigten Resourcen angefordert werden, wenn vorher noch keine einzige durch den ausführenden Auftrag belegt war. Zur Erläuterung: Serielle Komponenten belegen keine Resourcen. Damit würde jeder Aufruf einer nebenläufigen Komponente scheitern. Um dies zu vermeiden, werden die Resourcen in der nebenläufigen Komponente angefordert.
Serielle Komponenten müssen also damit rechnen eine Fehlermeldung über nicht verfügbare Resourcen zu bekommen.
Von unserem bisherigen Beispiel ausgehend, gibt es eine Applikation in der sich drei Dokumente befinden. Ein serielles Textdokument, ein nebenläufiges Tabellendokument und ein nebenläufiges Präsentationsdokument. Die Applikation selbst ist nebenläufig. Die Relationen gehen von der Applikation zu den Dokumenten und umgekehrt. Die Dokumente kennen sich nicht.
Fall 1:
In das serielle Textdokument soll eine Zeichenfolge
eingefügt werden. Da es sich um eine serielle Komponente
handelt, kann dieses Einfügen nicht von selbst ausgelöst
werden, es muß von einer nebenläufigen Komponente, hier
die Applikation, angestoßen werden. Die Applikation ist aber
verpflichtet die Resourcen vorher zu reservieren. Für diese
Abschätzung gibt es drei realistische Möglichkeiten. 1. Sie
reserviert nur das Textdokument selbst. Das bedeutet, das
Textdokument kann mit keinem anderen Objekt, auch nicht mit der
Applikation, kommunizieren. 2. Die Applikation und das Textdokument
wird reserviert. Es ist also nur der Zugriff auf die anderen
Dokumente verwehrt. 3. Alle Objekte werden reserviert. Geht es nach
dem Prealloc Resource Konzept muß 3. gewählt
werden. Aufgrund von Sicherheitsbeschränkungen werden wir aber
noch sehen, das serielle Komponenten in ihrer Auftragsbearbeitung
gestoppt werden können. Wenn der Abbruch eines Auftrags möglich
ist, spielt es aber keine Rolle durch wen (Resourcen oder Security)
dies geschehen ist.
Fall 2:
In das nebenläufige Tabellendokument soll eine
Zeichenfolge eingefügt werden. Dieser Auftrag kann von der
Applikation oder der Komponente selbst ausgelöst werden. In
jedem Fall müssen die Resourcen vor der Auftragsbearbeitung
reserviert werden. Man kann dies auch der Komponente überlassen
(siehe Anforderung 2.), aber man scheitert, wenn zwei Aufträge
zu einem Auftrag zusammengefaßt werden sollen. Dies passiert
z.B., wenn der Auftrag Text ersetzen aus den Aufträgen
Löschen und Einfügen besteht. Auf
jeden Fall wird nur das Tabellendokument selbst reserviert, da das
Einfügen keine Auswirkung auf andere Komponenten hat.
Fall 3:
In das nebenläufige Tabellendokument wird der
Applikationsname aus der Applikation eingefügt. Dazu fragt das
Tabellendokument nach den benötigten Resourcen, um den Namen zu
holen und ihn einzufügen. Zum Holen wird die Applikation
benötigt und zum Einfügen das Tabellendokument. Beide
müssen vor der Auftragsausführung reserviert werden.
Fall 4:
Das nebenläufige Tabellendokument fügt
selektierten Text aus dem seriellen Textdokument ein. Da das
Textdokument seinen Resourcebedarf nicht mitteilt, wird einer aus
Fall eins abgeschätzte Bedarf genommen. Man kann sehen, daß
der Auftrag für alle drei Möglichkeiten erteilt werden
kann. Seine Nebenläufigkeit wird dann durch die Abschätzung
eingeschränkt. Zusätzlich müssen natürlich die
benötigten Resourcen für das Einfügen geholt werden.
Alle müssen vor der Auftragsausführung reserviert werden.
Welche Konzepte können in einer objektorientierten Sprache wie c++ oder Java oder einer prozeduralen Sprache wie Fortran oder c eingesetzt werden, um Nebenläufigkeit zu erreichen.
Es gibt zwei Möglichkeiten eine Resource zu belegen. Das ist Exclusive (lesen, schreiben) und ReadOnly. Eine Resource kann von mehreren Aufträgen benutzt werden, wenn diese nur ReadOnly benötigen.
Es gibt Resourcen für die man die Resourceverteilung optimieren kann. Ein Objekt welches nicht geändert werden kann und das während der Auftragsausführung immer konsistent ist kann die Anforderung Exclusiv automatisch auf ReadOnly abschwächen. Dies lohnt sich, wenn man serielle Komponenten hat, die nichts über die Resourceanforderungen mitteilen. Als Beispiel möchte ich eine Instanz der Klasse String in Java nennen. Ein weitere Art von Resourcen fordern bei Aufträgen an sie 1. keine weiteren Aufträge an, 2. beenden sie die Aufträge schnell und 3. die Reihenfolge der Änderung an ihnen ist für andere nicht wichtig. Dies ist zum Beispiel bei der Speicherverwaltung in c der Fall. Diese Art der Resource darf zu einem späteren Zeitpunkt angefordert werden. Sie muß sofort benutzt und wieder freigegeben werden. Aus diesem Grund erledigen solche Resourcen das Anfordern und Freigeben selbst.
Bevor ein Auftrag ausgeführt werden kann, müssen alle von ihm benötigten Resourcen reserviert werden. Dies ist für einen Auftrag, der aus mehreren Teilaufträgen besteht, aufwendig. Eine Optimierung kann darin bestehen die Teilaufträge asynchron auszuführen. Allerdings dringt diese Verhaltensweise nach außen. Z.B. müssen Aufträge, die diesen dann asynchronen Auftrag nutzen, dann auch asynchron sein. Eine weitere Optimierung in der Autragsvergabe gibt es, wenn ein Autrag die Resourcervergabe nicht ändert. Es ist dann möglich mehr Aufträge vorzuziehen.
Es muß eine Queue geben, in die Aufträge eingefügt werden können. Konfliktfreie Aufträge können parallel ausgeführt werden. Achtung: Der Resourcebedarf eines Auftrages kann nur bestimmt werden, wenn alle benötigten Resourcen ReadOnly reserviert werden können, es sei denn kein vor ihm laufender Auftrag ändert die Resourcevergabe. Warum ist das so? Ein Auftrag kann eine Resource dahingehend ändern, daß danach andere Resourcen benötigt werden als vorher. Der vorher bestimmte Bedarf ist dann falsch.
Das Modell der Resourcen kann vergröbert oder verfeinert werden. In einem Tabellendokument könnte man jede einzelne Zelle zu einer Resource machen. Um die Komplexität der Resourcemodells zu vereinfachen kann man aber weiter alle Zellen der Dokument-Resource zuordnen. Wird also aus einer anderen Komponente die Zelle angefordert, wird automatisch das ganze Dokument reserviert. Daraus ergeben sich zwei Vorteile: 1. Für den Auftraggeber ist die Vergröberung transparent und 2. Kann die Resource an dem Objekt reserviert werden, das man ohnehin kennt.
Das Resource-Modell ist hierarchisch. Eine Resource kann nur einer Vergröberung zugeordnet werden. Die Tabellenzellen dürfen also nur dem Tabellendokument zugeordnet werden. Daraus ergibt sich, daß innerhalb einer solchen Hierarchie nebenläufig gearbeitet werden kann. Es dürfen dann aber keine Resourcen außerhalb der Hierarchie benutzt werden, selbst wenn diese reserviert sind.
Über den Benutzer müssen Daten abgefragt werden, die
über die Benutzung von Resourcen entscheidet (z.B.
Dateiname):
Ein solcher Auftrag muß in zwei Teilaufträge
unterteilt werden. Der erste erledigt die Abfrage. Danach werden alle
Resourcen freigegeben und dann fordert der zweite seine Resourcen und
wird bearbeitet. Eventuell kann ein solcher Auftrag den vorherigen
ersetzten, um zu verhindern das andere abhängige Aufträge
vor dem Aufgeteilten bearbeitet werden.
Ich habe mich bei einem Objekt als Listener angemeldet:
Es gibt
zwei Arten von Benachrichtigungen die ich erhalte. 1. Aufgrund der
Ausführung eines Auftrages und 2. einen Event von einer
nebenläufigen Komponente. Im ersten Fall überprüfe ich
den Resourcebedarf und führe dann den Auftrag aus. Im zweiten
Fall reserviere ich die benötigten Resourcen und führen den
Auftrag aus. Sind Resourcen reserviert, ist dies Fall eins, sonst
Fall zwei.
Ich bin Broadcaster:
Broadcaste ich aufgrund eines Auftrags tue
ich nichts weiter. Löse ich den Broadcast ohne Auftrag aus, muß
ich die Resourcen für die Listener bestimmen und sie vor dem
Aufruf reservieren. Die einzelnen Listener werden als unabhängig
betrachtet. Im Detail findet folgender Ablauf statt. 1. Die Liste der
Listener wird kopiert. 2. Für den ersten Listener wird der
Resourcebedarf ermittelt.
Die Basis für die Implementation des Resourcekonzeptes legen
die Klassen Resource,
ResourceList,
ResourceLockException,
Task, TaskManager,
TaskThread,
ThreadData
und das Interface Resourceable
fest. Um ein Objekt in das Resourcekonzept einbinden zu können
sind folgende Schritte notwendig:
1. Das Resourceable Interface
muß implementiert werden. 2. Ein Konstruktor mit der dem
Objekte zugewiesenen Resource. 3. Jede public Methode bekommt eine
*_Resource(...) Methode zur Seite, mit der der Resourcebedarf
ermittelt werden kann. 4. Innerhalb der public Methode wird der
Resourcebedarf ermittelt. 5. Mit dieser Information die als
ResourceListe vorliegt, wird eine Auftrag (Task) erzeugt. 6. Dieser
Auftrag wird beim TaskManager angemeldet. 7. Nach der Zuteilung durch
den TaskManager wird der Auftrag ausgeführt. 8. Alle Resourcen
und der Auftrag werden wieder freigegeben.
Diese Liste ist
detailliert aber nicht vollständig. In der Klasse
Resource steht imm eine Beispiel, welches aktuell sein sollte.
Folgende Programmierrichtlinien gibt es, um das Prealloc Resource Konzept in Java zu integrieren:
Es muß das Interface Resourceable implementiert werden. Mit Hilfe dieses Interfaces kann der Resourcebedarf eines Objektes erfragt werden. Diese Richtlinien gelten dann auch für die Superklasse.
???Es muß das Interface ModifyTestable implementiert werden. Damit kann überprüft werden, ob an den Resourcen Veränderungen vorgenommen wurden.
Nur Methoden die über die Lebensdauer des Objektes keine veränderten Werte liefern dürfen immer gerufen werden. Das sind zum Beispiel alle Methoden der Klasse java.lang.Object.
Um den Resourcebedarf einzelner Methoden genauer zu ermitteln kann eine Methode mit dem, um _Resource( ResourceList aRL, boolean bCheck, ... ) erweiterten Namen, gerufen werden. Ein Beispiel befindet sich in der Klasse Resource.
Diese Schnittstelle soll das Anfordern von Daten vereinheitlichen.
Das Problem, welches zu diesem Ansatz führte, ist das Lesen von
Daten über einen langsamen Internet-Stream. Das
bedeutet es werden Daten benötigt, die erst noch übertragen
werden müssen. Da das Pull-Modell immer einen eigenen Thread
vorraussetzt, um die restliche Anwendung nicht zu blockieren, habe
ich das Push-Modell gewählt.
Ziel: Für die
Implementation sollte es möglichst transparent sein, wie die
Daten herangeschafft werden. Als zweites soll die Schnittstelle für
denjenigen einfach sein, der alle Daten sofort bereithält.
Lösung:
Der Datenverarbeiter ist passiv. Das bedeutet, beim Entgegennehmen
der Daten beginnt er nicht sie zu verarbeiten. Dies muß extra
angestoßen werden. Zweitens, der Datenverarbeiter hält den
Status des Datenlieferanten. Dies können EOF, für alle
Daten gelesen, READY, für sind Daten da, PENDING, es kommen noch
weitere Daten und NO_SOURCE, es wurden nicht alle Daten verarbeitet
und es kommen keine weiteren Daten mehr. Achtung der Status
ist nur zu bestimmten Zeitpunkten gültig. Der Datenverarbeiter
darf nur im Zustand PENDING Daten bekommen. Diese Annahme schützt
ihn vor der Implementation eines Puffers. Das QueryData
- Interface ist die Spezifikation für dieses Verhalten.
Das Ziel ist nur eine Schnittstelle zu erstellen, mit der ein Objekt auf Änderungen überprüft werden kann. Da es für ein Objekt verschiedene Möglichkeiten gibt Änderungen an sich zu prüfen (z.B. Änderungszähler, Kopie), muß die Schnittstelle möglichst flexibel sein, um vielen Implementationen gerecht zu werden. Die Lösung sind zwei Methoden. Mit der einen (getModifyHandle()) kann der Zeitpunkt festgemacht werden, zu dem mögliche Änderungen überprüft werden sollen. Der Rückgabewert ist eine beliebiges Objekt, so daß in ihm die benötigte Überprüfungsinformation (z.B. der Änderungszähler) untergebracht werden kann. Danach kann mit der zweiten Methode (isModified(Object)) überprüft werden, ob eine Änderung stattgefunden hat. Das Interface für dieses Konzept heißt ModifyTestable .
LifeConnect ist die Kommunikation zwischen PlugIns, Applets und JavaScript. Die Kommunikation kann in beide Richtungen erfolgen.Unter JavaScript kann auf alle Systemklassen zugegriffen werden. Die Abbildung der JavaScript-Aufrufe nach Java ist die Aufgabe der Klasse CallJava. Dazu wird das in Java 1.1 implementierte Package java.lang.reflect benutzt. Da JavaScript eine nicht typisierte Sprache ist, werden die Werte nach JavaScript-Regeln in die entsprechenden Javatypen umgesetzt. Bezüglich der Sicherheit wird ein JavaScript-Programm auf die gleiche Stufe wie ein Applet gestellt. Um den Zugriff der Applets auf JavaScript zu gestatten, muß das HTML-Tag MYSCRIPT angegeben werden. Auf die Java-Klassen kann in JavaScript mit dem Prefix Package zugegriffen werden (sun, java und netscape benötigen keinen Prefix). Die Klassen netscape.plugin.Plugin, netscape.javascript.JSObject und netscape.javascript.JSException dienen zur Kommunikation von Java mit JavaScript.
Konvertierungstabelle anhand der Spezifikation JavaScript Language Specifications 3.1.2 TypeConversion
|
byte |
short |
char |
int |
long |
---|---|---|---|---|---|
Undef. |
Fehler |
Fehler |
Fehler |
Fehler |
Fehler |
Function |
(10) valueOf/error |
(11) valueOf/error |
(11) valueOf/error |
(12) valueOf/error |
(13) valueOf/error |
Object |
(10) valueOf/error |
(11) valueOf/error |
(11) valueOf/error |
(12) valueOf/error |
(13) valueOf/error |
Object (null) |
(10) 0 |
(11) 0 |
(11) 0 |
(12) 0 |
(13) 0 |
double |
(10) Zahl oder Fehler, wenn Bereichs-überschreitung |
(11) Zahl oder Fehler, wenn Bereichs-überschreitung |
(11) Zahl oder Fehler, wenn Bereichs-überschreitung |
(12) Zahl oder Fehler, wenn Bereichs-überschreitung |
(13) Zahl oder Fehler, wenn Bereichs-überschreitung |
boolean |
(15) 0/1 |
(15) 0/1 |
(15) 0/1 |
(15) 0/1 |
(15) 0/1 |
Leer String |
error |
error |
error |
error |
error |
String |
(10) error/ Zahl |
(11) error/ Zahl |
(11) error/ Zahl |
(12) error/ Zahl |
(13) error/ Zahl |
|
float |
double |
boolean |
String |
Object |
---|---|---|---|---|---|
Undef. |
Fehler |
Fehler |
false |
undefined |
null |
Function |
(14) valueOf/error |
(15) valueOf/error |
(15) valueOf/ true |
(15) JS-Code der Funktion |
(30) netscape .javascript. JSObject |
Object |
(14) valueOf/error |
(15) valueOf/error |
(15) valueOf/ true |
(15) valueOf / toString |
(30) Java-Cast/ error |
Object (null) |
(14) 0 |
(15) 0 |
(15) false |
(15) null |
(30) null |
double |
(14) Zahl oder Fehler, wenn Bereichs-überschreitung |
(30) Zahl |
(7) 0, NaN -> false !0, -+Infinite -> true |
(8) Zahl, NaN, Infinity oder -Infinity |
(9) Number/ error |
boolean |
(15) 0/1 |
(15) 0/1 |
(30) boolean |
(15) false/ true |
(15) Boolean/ error |
Leer String |
error |
error |
(15) false |
(30) String |
(15) String/ error |
String |
(14) error/ Zahl |
(15) error/ Zahl |
(15) true |
(30) String |
(15) String/ error |
Der Algorithmus zum mappen der polymorphen Methoden in Java:
1.
Die Anzahl der Parameter muß übereinstimmen.
2. Die
Parameter müssen, nach der obigen Tabelle, konvertiert werden
können.
3. Es gibt ein Punktesystem, nach dem die Methode
ausgewählt wird. Die Punkte stehen in Klammern in den
Tabelleneinträgen. Die Konvertierungspunkte für Zahlen sind
typabhängig und nicht wertabhängig. Dadurch ist
sichergestellt, das nicht unterschiedliche Methoden bei sich
ändernden Werten gerufen werden. Kommt es allerdings zu einem
Konvertierungsfehler (Überlauf), dann wird versucht eine andere
Methode zu finden.
4. Es wird vorausgesetzt, daß die
Methoden valueOf und toString keine
Seiteneffekte haben. Sie dürfen also beliebig oft aufgerufen
werden.
5. Es wird nur null auf eine Java-Array abgebildet.
Das Ziel dieses Abschnitts ist es Vorgehensweisen zu entwickeln, mit denen sich die Java Grundkonzepte testen lassen. Folgende Checkliste ist für jede Methode abzuarbeiten.
Zu jeder Klasse gibt es eine entsprechende Testklasse. Diese steht im Package test.... Der Name der Klasse wird mit Test erweitert. Beispiel: stardiv.memory.BitArray wird zu test.memory.BitArrayTest. Jede dieser Klassen hat eine Methode public static void main( String [] ). Diese Methode wird aufgerufen, um den Test aller Methoden anzustoßen. Der Test ist nicht interaktiv. Wird ein Fehler festgestellt, wird das Programm mit exit( -1 ) verlassen.
Jede Methode muß unabhängig von ihren Environment getestet werden. Alle Resourcen für die Methode werden als Dummy für den Test implementiert. Diese Forderung führt zu sauberen Schnittstellen, da ansonsten für den Test ja ganze Objekte implementiert werden müssen.
Das Testprotokoll protokolliert mit System.out.println. Vor dem Test der einzelnen Methoden wird in einer Zeile kurz über den Test informiert. Scheitert der Test, dann wird eine Fehlermeldung ausgegeben in der failed enthalten sein muß.
Um Defined Exception und Transacted Exception testen zu können, sollten die Resource und ModifyTestable Interfaces implementiert werden. Es kann damit automatisch geprüft werden, ob sich eine Resource unerlaubter Weise geändert hat.
Lebendig: Ein System wird als lebendig bezeichnet, wenn alle in ihm befindlichen Aufträge fertiggestellt werden können. Sie sich also nicht in einer Verklemmung oder einem Race befinden.