Java_2B - DHBW Mosbach

Werbung
(74)
Übergang zur Schnittstellenvererbung
Einfachvererbung versus Mehrfachvererbung
Interface = Schnittstellenspezifikation
Mehrfachvererbung bei Interfaces
Verwendungsregeln
Polymorphie / Entkopplung / Design mit Interfaces
Annotationen
Bis Java 7 blieben Interfaces stabil.
Java 8 führt dort (leider) Implementierungsaspekte ein …
© H.Neuendorf
(s.u.)
Warum keine Klassen-Mehrfachvererbung ?
Java :
Klassen-Einfachvererbung - jede Unterklasse hat nur eine direkte Oberklasse
Realität :
Auch Mehrfachvererbung
Kind → (Vater + Mutter)
(75)
aber …
Mehrfachklassenvererbung macht logische + technische Strukturen komplexer / schwieriger
Erhöhter Speicherbedarf + Verwaltungsaufwand für JVM → VMT ( virtual method tables )
Rautenproblem der OO :
Basis
Mehrfachvererbung für Klasse Unter_2 :
m()
Erbt m() und a "zweimal" :
Über Klasse Unter_1a und über Unter_1b
a
Welcher Weg ist gültig ?
Unter_1a
Unter_1b
m()
m()
m() in Unter_1a und Unter_1b überschreibbar !
Welche Variante bei Aufruf aus Unter_2 verwenden ?
⇓
Lösung : Keine Mehrfachvererbung
Unter_2
??
© H.Neuendorf
Interface-Konzept !
Interface = Schnittstellenspezifikation
(76)
In Idealform enthält ein Interface ausschließlich abstrakte Methoden - basta !!
(In Java können jedoch (leider?!) auch weitere Bestandteile aufgenommen werden …)
Ein Interface definiert eine Schnittstelle = Typ - nicht seine Implementierung !
Liefert Spezifikation von Fähigkeiten / Verhaltensweisen = Vertrag
Interfaces geben Verhalten (=Methoden) vor - keinen Zustand !
Denn sie enthalten keine nicht-statischen Attribute
1. Interfaces können von mehreren Interfaces erben
⇒ extends
Mehrere Schnittstellenbeschreibungen können in einem Interface zusammengefasst werden
2. Klassen können mehrere Interfaces implementieren :
Neues Schlüsselwort
implements
3. Jede implementierende Klasse ist typkompatibel zu Interface !
Somit können ihre Objekte überall dort eingesetzt werden, wo der IF-Typ gefordert ist !
© H.Neuendorf
(77)
Interface = Schnittstellenspezifikation
Inhalt bis incl. Java 7 :
interface Konto {
public static final double MIN = 10.0 ;
Abstrakte Methoden + Statische Konstanten
public abstract void einzahlen( double betrag ) ;
public abstract void abheben( double betrag ) ;
Alle Methoden
public abstract
Alle Attribute
public static final
}
class Sparkonto implements Konto {
private double saldo ;
public Sparkonto( ) { /*…*/ }
Schlüsselwörter public, abstract, static final
müssen nicht explizit angegeben werden …
public double getSaldo( ) { return saldo ; }
public void einzahlen( double betrag ) {
… vom Compiler automatisch gesetzt.
saldo = saldo + betrag ;
}
Konstanten - müssen im IF initialisiert werden !
public void abheben( double betrag ) {
if( saldo - betrag > MIN ) saldo = saldo - betrag ;
Implementierte Methoden müssen public sein !
Eine vom IF erbende Klasse, die nicht alle
Interfacemethoden implementiert, enthält
abstrakte Methoden + ist somit abstract !
© H.Neuendorf
else /* … */
}
}
Nachträgliche Änderungen an
Interfaces erzwingen
Anpassungen in allen
implementierenden Klassen !!
IF - Strukturierung + Modellierung
Modellierung Mehrfachvererbung durch
Interface-Hierarchie : extends
(78)
Fahrzeugantrieb
Diesel
Elektro
Hybrid
Interfaces erlauben Modellierung komplexer
Zusammenhänge :
→
Anforderungs-Spezifikation
IF-Hierarchie bildet Zusammenhang ab
Keine Implementierung von Methoden - aber
Modellierung + Strukturierung
Klassen, die IF implementieren, übernehmen
dadurch Strukturierung des Sachverhalts
Reihenfolge - erst Erben / dann Implementieren :
class A extends B implements IFC, IFD { /* … */ }
© H.Neuendorf
interface Fahrzeugantrieb {
int getLeistung( ) ; int getGewicht( ) ;
}
interface Diesel extends Fahrzeugantrieb {
double getHubraum( ) ; double getVerbrauch( ) ;
}
interface Elektro extends Fahrzeugantrieb {
double getBatteriekapazität( ) ;
}
interface Hybrid extends Diesel , Elektro {
String getKopplungsart( ) ;
}
class HybridAntrieb implements Hybrid {
public int getLeistung( ) { … }
public double getBatteriekapazität( ) { … }
// …
}
Interfaces - Strukturierung + Modellierung
Schnittstellenvererbung
(79)
Interfaces :
statt
Spezifikation + Modellierung
Implementierungsvererbung
Abstrakte Vertragsdefinition
⇓
Strukturierung von Software !
Klassen :
Konkrete Implementierung
Interfaces können nicht instanziiert werden
Sollen durch implementierende Klasse konkretisiert werden
Extreme Form : Marker-Interfaces
Ohne jeden Inhalt - Vertrag nur durch
ihre Dokumentation festgelegt
Unterschied Interface - abstrakte Klasse :
1. Abstrakte Klassen können Implementierung enthalten
2. Abstrakte Klassen können private + finale Methoden enthalten
3. Klassen können nur von einer abstrakten Oberklasse erben
Aber Klassen können beliebig viele Interfaces implementieren
4. IFs erlauben Mehrfachvererbung
© H.Neuendorf
Es wird kein Verhalten definiert - dh keine
Methoden + Konstanten enthalten.
Vertrag befindet sich in Dokumentation,
die die Erwartungen beschreibt, die
implememtierende Klasse erfüllen sollte
Bsp : Serializable, Cloneable …
Interfaces - Rautenprobleme
Methoden – Regeln :
(80)
X
(IF X,Y)
Gleicher Methodenname, gleicher oder verschiedener Rückgabetyp,
Y
Z
unterschiedliche Parameterlisten :
Klasse Z muss entsprechend viele gleichnamige überladene Methoden implementieren
Gleicher Methodenname, gleicher Rückgabetyp, gleiche Parameterlisten :
Klasse Z muss nur diese eine Methode implementieren
Gleicher Methodenname, gleiche Parameterlisten unterschiedlicher Rückgabetyp :
Nicht erlaubt - Compilerfehler !
Identische Konstanten-Namen - Regeln :
Konflikt durch Qualifikation des Interface-Namens aufgelöst
interface Skat
{ int kartenZahl = 32 ; }
Sinn der Konstanten: Vereinheitlichung
der späteren Implementierungen durch
Bezug auf einheitliche (z.B. n.w.) Konstanten
// IF-Konstanten auch in nicht
interface Poker { int kartenZahl = 52 ; }
// implementierenden Klassen verwendbar :
class Z implements Skat, Poker {
class A {
/* …*/ Skat.kartenZahl
/* …*/ Skat.kartenZahl
/*…*/
/*… */ Poker.kartenZahl /*…*/
/*… */ Poker.kartenZahl /*…*/
}
© H.Neuendorf
/*…*/
}
Programmieren mit Interfaces
(81)
Interfaces legen keine Implementierungsdetails fest :
⇒ Ihre Methoden können nicht mit native, synchronized oder strictfp modifiziert werden
Würde Implementierung vorschreiben – dies ist allein Sache der implementierenden Klasse !
⇒ Interfaces dürfen keine Konstruktoren vorschreiben !
Methoden können nicht final sein
→ denn sie müssen erst noch in einer Klasse implementiert werden
Methoden können nicht static sein → denn statische Methoden können nicht abstract sein
Methoden dürfen mit throws-Klausel Exception-Verhalten beschreiben
Interface-Konstanten können Objekte sein
… aber kein guter Stil, da dadurch IF von konkreter Implementierung abhängig
class A { /* … */ }
interface IFEuro {
public static final A a = new A( ) ;
public abstract double inDM( double euros ) throws MyException ;
}
© H.Neuendorf
(83)
Interfaces nicht als Konstantendeponie !
interface PhysicalConstants {
public static final double NA = 6.022e23 ;
package science ;
// Bessere Lösung !
public final class PhysicalConstants { // Tool
public static final double KB = 1.38e-23 ;
private PhysicalConstants( ) { }
// no objects!
public static final double ME = 9.11e-31 ;
public static final double NA = 6.022e23 ;
public static final double KB = 1.38e-23 ;
}
public static final double ME = 9.11e-31 ;
Interne Verwendung von Konstanten sollte ein
Implementierungsdetail der Klasse sein.
Bei Implementierung eines entsprechenden
"Konstanten-Interfaces" werden die Konstanten
jedoch Teil der öffentlichen Schnittstelle der
implementierenden Klasse ("implementation
details leak into the class's exported API").
Verwirrt Benutzer der Klasse, da er keinen Sinn
in Werten sieht, die nur intern benötigt werden.
Die Schnittstelle der Klasse und ihrer
Unterklassen wird mit den IF-Konstanten
verschmutzt.
}
import science.PhysicalConstants ;
Nichtinstanziierbare
Tool-Klasse ist
bessere Lösung !
class Test {
public double atoms( double mol ) {
return PhysicalConstants.NA * mol ;
}
// …
}
Interfaces sollten verwendet werden, um
Typen + Schnittstellen zu definieren !
Sie sollten nicht dem bloßen Export von
Konstanten dienen !
© H.Neuendorf
Interfaces :
Entkopplung :
Bedeutung als Entkoppler + Vermittler
(84)
durch Interfaces als Mittel der Abstraktion
Interne Implementierungen interessieren nicht – nur die Aufruf-Schnittstellen !
Interface als Mittler zwischen Aufrufer
(Client) und Gerufenem (Server) :
Klasse A
Klasse B
Klasse A bildet mit Interface I eine syntaktisch
korrekte kompilierbare Einheit – unabhängig von
konkreter Implementierung der Klasse B
Klasse B bildet mit Interface I eine syntaktisch
korrekte kompilierbare Einheit – unabhängig von
konkreten Aufrufen durch Klasse A
Client
Interface I
Server
Durch Interface I wurden Klasse A und
Klasse B voneinander entkoppelt :
Implementierung B ist austauschbar
Indirekter Zugriff durch A
Erst in der Anwendung werden die Typen miteinander
verbunden - Einsetzen konkreter Objekte bei Aufrufen:
I impl = new B( );
© H.Neuendorf
A a = new A( );
a.m( impl );
Interfaces definieren Vertrag zwischen
Konsumenten und Produzenten
Programmieren mit IFs
Interface = Typ
Auf diesen kann man sich bei MethodenImplementierung beziehen :
Parameter, Rückgabewert, Variablen können
von Interface-Typ sein !
Interface-Referenz wird Objekt einer
implementierenden Klasse zugewiesen *)
Darauf nur die im IF definierten Methoden +
Konstanten zugreifbar !
Erhöhte Typsicherheit & flexiblere
Austauschbarkeit der Implementierung !
⇓
Programmierung via Interfaces
Konkrete Aufrufe mit Objekten der
implementierenden Klasse(n)
© H.Neuendorf
(85)
interface IEuroSF {
public static final double kurs = 1.14 ;
public abstract double inSF( double euroBetrag ) ;
}
//////////////////////////////////////////////////////////////////////////
class User { // "Client"
public static double rechne( IEuroSF iE , double e ) {
double s = iE.inSF( e ) ;
return s ;
Kennt Implementierer nicht !
}
}
///////////////////////////////////////////////////////////////////////////
class Eurorechner implements IEuroSF { // "Server"
public double inSF( double euroBetrag ) {
return euroBetrag * IFEuroSF.kurs ;
}
Kennt User nicht !
}
/////////////////////////////////////////////////////////////////////////////
class IFTest { // "Anwendung"
public static void main( String[ ] args ) {
IEuroSF eR = new Eurorechner( ) ;
// *
double d = User.rechne( eR , 200 ) ;
}
}
Interfaces + Design
(86)
<< interface>>
Form
double flaeche()
Interface definiert Typ - auf den sich System bezieht
Interface beschreibt Konzept - nicht konkrete Impl.
Klassen implementieren das IF = den Typ.
Das aufrufende System ist nur von der Schnittstelle
abhängig – muss dank Polymorphie die Klassen der
beteiligten Objekte nicht kennen.
Kreis
Viereck
Dreieck
Einbau neuer System-Features :
Prinzip :
Entkopplung von Schnittstelle von Implementierung
Nicht durch Modifikation existierenden
Codes, sondern …
Entkopplung des "Was" vom "Wie"
… durch Erweiterung mittels neuer Klassen
Beliebige konkrete Implementierungen verwendbar
Verbesserung der Entwurfsqualität :
System nicht von Implementierungsdetails abhänig,
sondern nur von grundlegenden Konzepten.
© H.Neuendorf
Polymorphie - statt bedingtem Verhalten
mittels switch-case-Konstrukten :
Je nach übergebenem Objekttyp zeigt Methode
anderes Verhalten – ohne dass dies in der
Methode durch verschiedene Cases explizit
behandelt werden müsste
Interfaces + Design
(87)
<< interface>>
Form
double flaeche()
// OCP : Stabiles Coding
// Auch bei weiteren Implementierungen des
// IFs Form ergeben sich hier keine Änderungen !
class Geometrie {
private Form[ ] container = new Form[ 100 ] ;
Kreis
Viereck
Dreieck
private int pos = 0 ;
public void add( Form frm ) {
container[ pos ] = frm ;
pos++ ;
}
public void ausgabe( ) {
for( int i=0; i<container.length; i++ ) {
Form frm = container[ i ] ;
Einbau neuer System-Features :
Modifikationsfreie Erweiterung des Systems mittels
neuer Klassen (zB: Raute, Ellipse …) ⇒
Veränderungen durch Hinzufügen neuen Codes statt
durch Änderung bestehenden Codes
if( frm != null )
IO.writeln( "Flaeche = " + frm.flaeche( ) ) ;
}
OpenClosedPrinciple OCP :
Systeme sollen offen für Erweiterungen sein …
}
… aber abgeschlossen gegenüber Änderungen
// …
Stabilität + Flexibilität : Kein Änderungsbedarf am
Großteil existierenden Codings
}
© H.Neuendorf
Pattern : Interface – Abstrakte Klasse – Implementierende Klasse
interface IF {
m1( )
m3( )
(88)
Formuliert Vertrag + Typ = Verhalten =
Schnittstelle
m2( )
m4( )
Framework :
→
Verlangt nach Typ IF
m5( ) …
}
abstract class AbstractIF implements IF {
// abstract public void m1( ) ;
// abstract public void m2( ) ; Abstrakte skeletal-implementation-class
als Implementierungshilfe :
public void m3( ) { }
Implementiert Vertrag bereits teilweise –
public void m4( ) { }
eventuell mit leerer Implementierung
public void m5( ) { }
Grundlegende Methoden bleiben abstrakt,
…
verwendende Methoden vorimplementiert
}
Instanzen entsprechen
dem vom Framework
geforderten Typ !
Bsp :
class MyVersionOfIF extends AbstractIF {
public void m1( ) { }
Konkrete Klasse : Muss nur noch Teil der
public void m2( ) { }
Methoden implementieren bzw. zur
public void m4( ) { }
individuellen Anpassung überschreiben
…
Weniger mühsam, als direkt mit IF zu beginnen
}
© H.Neuendorf
java.util Collection Framework
IF
Collection
AK
AbstractCollection
AbstractList …
Pattern : Interface zur Formulierung von Factories
interface Konto { /* … */ }
class Giro implements Konto { /* … */ }
class Sparkonto implements Konto { /* … */ }
class Bauspar implements Konto { /* … */ }
// … und viele andere mehr …
abstract class KontoFactory {
public static Konto createKonto( String type ) {
if( type.equals("G") ) return new Giro( /* … */ ) ;
if( type.equals("S") ) return new Sparkonto( /* … */ ) ;
if( type.equals("B") ) return new Bauspar( /* … */ ) ;
// …
}
}
Das Interface als allgemeiner Typ
Konkrete Klassen, die alle den Interface-Typ
spezifisch implementieren
Eine abstrakte Factory-Klasse, die passende
Objekte liefert, ohne dass Namen und die
Existenz der dahinterstehenden Klassen
bekannt sein müssten !
Vorteil von Factories :
Flexibel anpassbares Erzeugen von Instanzen,
ohne Details zu exponieren.
class Anwendung {
public static void main( String[ ] args ) {
String type = IO.promptAndReadString( "Kontotyp (S/B/G/…) ? ") ;
Konto k = KontoFactory.createKonto( type ) ;
// Objekt verwenden …
}
}
© H.Neuendorf
(89)
Anwendung, die sich auf das
beschreibende Interface und die
Dienste der Factory verlässt, ohne die
dahinterstehenden implementierenden
Klassen zu kennen !
Beispiel:
Interfaces
java.lang.Comparable
(90)
java.util.Comparator
Vergleichsmethoden :
compareTo( ) und compare( ) implementieren eine totale Ordnungsrelation mit gleicher Semantik
a.compareTo( b )
compare( a, b )
<0
=0
>0
bedeutet:
a<b
a gleichgroß b
a>b
Absolutwerte <0 oder >0 egal –
keine weitere Semantik einbauen !
Totale Ordnung :
Allerdings unterschiedliche Signatur der Vergleichsmethoden :
public interface Comparable {
public interface Comparator {
public int compare( Object o1, Object o2 ) ;
public int compareTo( Object o ) ;
}
// … weitere Details hier unwichtig
Alle Elemente einer
Menge können
aufgrund einer
Größer-GleichRelation linear
angeordnet werden
}
Implementierung von Comparable
bei "natürlicher" Ordnung als Teil
der Klassenlogik
(zB Bruch, Integer, Complex)
Implementierung von Comparator für weitere
Sortierkriterien
In Klasse - oder durch separate Comparator-Klassen :
Definieren weitere Kriterien - davon evtl. beliebig viele …
Ab Java5 generisch für Referenztyp T :
interface Comparable<T>
interface Comparator<T>
© H.Neuendorf
Beispiel: Interfaces
java.lang.Comparable
java.util.Comparator
import java.util.* ;
import java.util.*;
class Mitarbeiter implements Comparable {
class Sorted {
public int persNr ; // eindeutige Ordnung
(91)
public static void main( String[ ] args ) {
public String name ;
Mitarbeiter[ ] m = new Mitarbeiter[4] ;
public Mitarbeiter( int nr, String n ) { /* … */ }
m[0] = new Mitarbeiter( 11, "Anna" ) ;
// …
m[1] = new Mitarbeiter( 5, "Ursula" ) ;
public int compareTo( Object o ) {
m[2] = new Mitarbeiter( 7, "Wolf" ) ;
Mitarbeiter m = (Mitarbeiter) o ;
m[3] = new Mitarbeiter( 13, "Bert" ) ;
return this.persNr – m.persNr ;
// Unsortierte Ausgabe:
System.out.println( Arrays.toString( m ) ) ;
}
}
// Sortieren nach compareTo()
// Weiteres Sortierkriterium: name
Arrays.sort( m ) ;
class NameComparator implements Comparator {
System.out.println( Arrays.toString( m ) ) ;
public int compare( Object p, Object q ) {
String s1 = ( (Mitarbeiter) p ).name ;
// Sortieren nach NameComparator
String s2 = ( (Mitarbeiter) q ).name ;
Arrays.sort( m, new NameComparator( ) ) ;
return s1.compareTo( s2 ) ;
System.out.println( Arrays.toString( m ) ) ;
}
}
}
© H.Neuendorf
}
Strategy-Pattern → Inversion of Dependencies
Dependeny-Inversion Principle : policy should not depend on details
(92)
(R.C. Martin)
1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
2. Abstractions should not depend on details. Details should depend on Abstractions.
High level modules contain the important policy decisions and business models of an application.
Prinzip :
Abhängigkeit von konkreten (und somit veränderlichen) Klassen minimieren
Veränderliche Klassen hinter grundlegenden Interfaces verbergen
Client gibt erforderliches Interface vor – dieses ist von tieferen Schichten zu implementieren
Prinzip: Entwerfe ein System Top Down
(gemäß Vorgaben der höheren Schichten),
statt Bottom Up (gemäß Vorgaben der tieferen
Schichten)
Keine triviale Forderung – denn meist ist es
historisch gewachsen anders:
Quelle: R.C.Martin, Agile Software Development, S.128f
Höhere Schichten nutzen "traditionellerweise"
direkt die Services niedrigerer Schichten, rufen
deren Methoden direkt auf, hängen somit von
diesen direkt ab …
Deshalb die Forderung nach Inversion
(Umkehr) der "üblichen" Abhängigkeiten !
© H.Neuendorf
Beispiel: Schalter & Lampe
(93)
Schalter (policy) und Lampe (detail) in traditioneller Abhängigkeit :
Schalter-Klasse hängt direkt von Klasse Lampe ab ⇒
Schalter kann nur Lampen-Objekte schalten - keine anderen Geräte.
Jede Änderung in Klasse Lampe beeinflusst Klasse Schalter.
Die Policy (high level) hängt von den Details (low level implementation) ab.
class Schalter {
Schalter
+ schalte()
Lampe
private Lampe meineLampe ;
+ schalteEin()
+ schalteAus()
public Schalter( Lampe lp ) {
meineLampe = lp ;
}
Ziel : Inversion der Abhängigkeiten :
public void schalte( ) {
Schalter-Klasse schreibt den Devices das geforderte
Verhalten durch ein Interface vor ⇒
if( /* … */ ) meineLampe.schalteEin( ) ;
else meineLampe.schalteAus( ) ;
Die Devices hängen auf diese Weise von den
Forderungen des Clients ab !
}
}
Analogie : Manger in Zentrale bekommt aus vielen Filialen Reports in
jeweils anderem Format. Muss sich auf diese einstellen.
class Lampe {
public void schalteEin( ) { /* … */ }
"Dreht nun den Spieß um", indem er den Filialen ein festes Berichtsformat
("Formular" = IF) vorschreibt, das diese einhalten müssen.
Dadurch ist auch die Hinzunahme weiterer Filialen kein Problem mehr,
erfordert keine konzeptionelle Zusatzarbeit !
© H.Neuendorf
public void schalteAus( ) { /* … */ }
}
(94)
Beispiel: Schalter & Lampe
Schalter
Inversion der Abhängigkeiten :
+ schalte( )
Schalter-Klasse fordert Devices-Verhalten durch Interface
<< interface>>
Schaltbar
+ schalteEin( )
+ schalteAus( )
Devices hängen nun von Forderungen des Clients ab
Schalter kann alle Geräte kontrollieren, die das Interface (grundlegende
Verhaltensabstraktion) implementieren – auch noch gar nicht "Erfundene"
Lampe
Motor
Deren Details spielen für Systemstruktur keine Rolle mehr
Filter
Systemstruktur viel flexibler
Änderung in Klasse Lampe beeinflusst Klasse Schalter strukturell nicht.
class Schalter {
private Schaltbar meinServer ;
public Schalter( Schaltbar s ) {
meinServer = s ;
}
public void schalte( ) {
if( /* … */ ) meinServer.schalteEin( ) ;
else meinServer.schalteAus( ) ;
}
}
class Lampe implements Schaltbar {
public void schalteEin( ) { /* … */ }
public void schalteAus( ) { /* … */ }
}
class Motor implements Schaltbar {
public void schalteEin( ) { /* … */ }
public void schalteAus( ) { /* … */ }
}
Das Interface erlaubt, beliebige weitere nutzende
Clients (Trigger, Hebel, Taster …) zu definieren.
© H.Neuendorf
Erweiterung vs. Verfeinerung von Interfaces
interface S1 { /* Methoden m1, m2 */ }
(95)
interface S extends S1, S2, S3 { /* … */ }
interface S2 { /* Methoden m4, m5 */ }
interface S3 { /* Methoden m3 */ }
Typmäßige Detailsichten
auf Gesamttyp
class A implements S { /* … */ }
Der Gesamttyp und
seine Implementierung
Interface kann komplex sein und zahlreiche Methoden enthalten
class UserX { // nutzt nur m1,m2
S1 view1 = new A( ) ;
view1.m1( ) ; view1.m2( ) ;
}
class UserY { // nutzt nur m3
S3 view3 = new A( ) ;
view3.m3( ) ;
}
© H.Neuendorf
Entwickler interessiert sich jedoch evtl. nur für einen Teil davon möchte die anderen Methoden typmäßig gerne ausblenden, so
dass diese gar nicht aufrufbar sind
Durch Detail-Interfaces ("Sichten") wird die Gesamtschnittstelle für
verschiedene Entwickler aufgeteilt, so dass nur die benötigten
Methoden sichtbar sind. Um die große Schnittstelle kümmern sich
nur wenige Experten.
Jede Sicht ist ein eigenes Interface; die gesamte Schnittstelle S ist
eine Erweiterung aller Detail-Sichten - und kann bei Bedarf
zusätzliche Methoden enthalten, die zu keiner Sicht gehören.
Vorteile und Probleme von Interfaces
(96)
lt is, generally speaking, impossible to add a method to a public interface without breaking all existing
classes that implement the interface. Classes that previously implemented the interface will be missing
the new method and won't compile anymore.
You could limit the damage somewhat by adding the new method to the skeletal implementation at the
same time as you add it to the interface, but this really wouldn't solve the problem. Any implementation
that didn't inherit from the skeletal implementation would still be broken.
Public interfaces, therefore, must be designed carefully. Once an interface is released and
widely implemented, it is almost impossible to change. You really must get it right the first time.
If an interface contains a minor flaw, it will irritate you and its users forever. If an interface is
severely deficient, it can doom an API.
The best thing to do when releasing a new interface is to have as many programmers as possible
implement the interface in as many ways as possible before the interface is frozen. This will allow you
to discover flaws while you can still correct them.
Finally, you should design all of your public interfaces with the utmost care and test them thoroughly by
writing multiple implementations.
Joshua Bloch, Effective Java
© H.Neuendorf
S.97
Vorteile von Interfaces
– Denken + Entwickeln via Schnittstellen
(97)
1. Interfaces fördern den modularen Aufbau von Systemen
→ Prinzip Separation of Concerns unterstützt – Anforderungsverteilung auf verschiedene Interfaces
→ Wartbarkeit gefördert durch klar definierte konzeptionelle Einheiten
2. Interfaces fördern das Information Hiding schon beim Entwurf von Systemen
→ Kein "Drauflos-Implementieren" - sondern thematische Strukturierung
→ "Rauschen der Implementierung" kommt erst später
→ Konzentration auf Sinn und Semantik der Schnittstelle :
Konzentration auf saubere + sinnvolle Signaturen
3. Interfaces sind die eigentlichen Träger der Softwarearchitektur
Sie verbessern die spätere Implementierung
→ Implementierung hat klare Basis + eindeutige semantische Zielrichtung
→ Lose Kopplung zwischen Schnittstelle + Implementierung
→ Leichtere Austauschbarkeit der Implementierung
(Testen, Warten, Upgrade)
→ Open-Closed-Prinzip: Software soll offen sein für Erweiterungen …
… aber abgeschlossen für Änderungen (Konstanz der Schnittstelle)
© H.Neuendorf
Grundsätze des OO-Designs
Grundeinheit des OO-Programmierens ist die Klasse
Aber Grundeinheit des OO-Entwurfs ist der Typ !
(98)
Abstrahiere von der konkreten Implementierung – denn diese ist austauschbar
Programmiere gegen Interfaces – nicht gegen konkrete Klassen
Abhängigkeiten von Abstraktionen herstellen – nicht von Konkretisierungen
Hohe Abstraktion statt vorschneller Konkretisierung – alles Konkrete veraltet schnell
Implementierungen müssen austauschbar sein + Erweiterungen leicht möglich sein
Denke in Verträgen + Service-Anforderungen – nicht in Details von Algorithmen
Entkopple Konsumenten und Produzenten durch Interfaces (Service-Verträge)
Garantiert werden keine Implementierungsdetails , sondern …
… die Einhaltung von Service-Verträgen, die in Interfaces festgeschrieben sind
Die Seele des Ganzen ist die sinnvolle Typisierung – nicht die konkrete Implementierung
Erfinde das Rad nicht zweimal – orientiere dich an Design-Patterns
Modellieren ist grundlegender als Implementieren
© H.Neuendorf
(…nicht mehr oder weniger wichtig)
Kennzeichen echt Objektorientierter Sprachen
1. Klassenkonzept
Zusammenfassung von Daten + Funktionalität in syntaktischer Einheit
2. Kapselung
Kontrolle des Zugriffs mittels eingeschränkter Sichtbarkeit - public / private / etc.
3. Vererbung
Implementierungsvererbung :
Weitergabe von Implementierung von Ober- an Unterklassen
4. Polymorphie
Schnittstellenvererbung :
Weitergabe von Typisierung von Oberklassen an Unterklassen
© H.Neuendorf
(99)
@Annotations :
Meta-Informationen zu Klassen + Methoden
(101)
Annotations do not directly affect program semantics, but they do affect the way programs are treated by tools […].
Annotations can be read from source files, class files, or reflectively at run time.
Typical application programmers will never have to define an annotation type, but it is not hard to do so.
Als spezielle Art von "Interface" Teil von Java ⇒
Compiler prüft syntaktische Korrektheit
@interface Test{ }
Können Elemente enthalten, die bei Verwendung
mit passenden Werten zu füllen sind :
@interface Copyright {
String author( ) ;
int year( ) ;
Nur primitive Typen, Strings, Arrays
}
Elemente ohne Parameter / Exceptions !
@Copyright( author="SAP AG" , year = 2013 )
Default-Werte können hinterlegt werden
public class SomethingNew {
@Test void tuWas( ) { /* nur Test */ }
Annotierbarkeit :
Zulässig nur vor Package, Klasse, Interface,
Methode, Attribut, Parameter, lokale Variable
© H.Neuendorf
}
@Annotations :
Meta-Informationen zu Klassen + Methoden
Marker-Quelltext-Auswertung durch Tools zur Erstellung von Ressourcen
→
Javadoc
JUnit …
Teilweise durch Compiler gecheckt
→
@Override
@SuppressWarnings("unused")
@Deprecated
Einkompiliert in Bytecode - zur LZ-Auswertung via Reflection
→
java.lang.annotation
Liefert Frameworks + Containern Informationen
→
EJB :
@Stateless
@Remote
…
Parametrisierbar → Annotationen können selbst Meta-Annotationen besitzen
Auslesbarkeit / Anwendbarkeit / Übernahme in Javadoc / Vererbbarkeit / …
durch Meta-Annotationen einschränkbar
Paket
java.lang.annotation
Enthält zahlreiche Meta-Annotationen
© H.Neuendorf
(102)
@Annotations :
@interface Mark{ }
(103)
Weitere Techniken …
// keine Elemente, reiner Marker
// Alle Annotationen beziehen sich auf TestKlasse
// oder auch : Mark( )
@Mark
@interface Revision {
@ClassVersionInfo(
int major( ) default 1 ; // Hinterlegen Defaults
created = "20.11.2013",
int minor( ) default 0 ;
createdBy = "H.Neuendorf",
}
lastModified = "22.11.2014",
lastModifiedBy = "H.Neuendorf",
@interface ClassVersionInfo {
revision = @Revision( minor = 2 )
String created( ) ;
String createdBy( ) ;
)
String lastModified( ) ;
@BugsFixed(
String lastModifiedBy( ) ;
// Defaults
bugsID = { "ArrayIndex" , "NP", "IO-Exception" }
Revision revision( ) ;
)
}
class TestKlasse {
@interface BugsFixed {
String[ ] bugsID( ) ;
}
© H.Neuendorf
Für jede Annotation
wird eigenes .classFile angelegt
// …
}
Durch Annotationen kann man
Intentionen ausdrücken, die von Tools /
Frameworks berücksichtigt werden
Ansätze der Aspekt-Orientierten
Programmierung
(104)
Interface-Erweiterungen in Java 8
Aufnahme von Implementierungen
Statische Implementierte Methode
Default-Implementierungen
Rautenproblem + andere Kollisionen
In seiner Idealform enthält ein Interface
ausschließlich abstrakte Methoden - basta !!
Erweiterung des IF-Konzepts beruht nicht auf vormailger Unvollständigkeit,
sondern wurde durch Erweiterungen bei Lambda-Expressions erzwungen !
© H.Neuendorf
Java 8 Interfaces - Statische Methoden
Interfaces können statische implementierte Methoden enthalten (Tool / Utility)
Modifizierer static ist explizit anzugeben !
Statische Methoden des IFs haben Zugriff auf die Konstanten des IFs
Die statischen Methoden sind nur auf dem Namen des Interfaces aufrufbar !
Aufruf auf implementierender Klasse und deren Objekten ist nicht zulässig !
interface IFPrice {
public static final int MAX_PRICE = 1000 ;
public static boolean isValid( double p ) {
return p > 0 && p < MAX_PRICE ;
}
boolean b ;
b = IFPrice.isValid( 850.0 ) ; // ok !
b = Price.isValid( 850.0 ) ; // Fehler !
Price pObj = new Price( ) ;
b = pObj.isValid( 850.0 ) ; // Fehler !
public abstract double calcPrice( ) ;
}
Nun droht Mißbrauch des IF-Konzepts als
Tooling-Deponie - besser :
class Price implements IFPrice { /* …*/ }
Finale Tool-Klasse mit privatem Konstruktor
© H.Neuendorf
(105)
Java 8 Interfaces - Default Implementierungen
Interfaces können nicht-statische Methoden mit Standard-Implementierung enthalten
Default-Methoden machen gewöhnliche IFs zu Erweiterten Schnittstellen
Verhalten sich wie Methoden in abstrakten Klassen hinsichtlich Zugriff auf IF-Elemente
Sinn : Hinzufügen weiterer Methoden zu IF ohne Implementierer zu invalidieren
Erweiterung von IFs (Retrofitting) jederzeit kompatibel möglich !
Implementierende Klassen können Default-Implementierung übernehmen
Müssen diese nicht überschreiben - können es aber natürlich !
interface IFPrice {
// Schlüsselwort / Modifizierer
default
abstract public double calcProfit( ) ;
default public double calcPrice( ) { return 100.0 ; }
default public void test( ) { } // void-Methode mit leerer Standard-Implementierung - ok !
default public int check( ) { throw new RuntimeException( "not implemented" ) ; } // ok !
default public boolean hasProfit( ) { return calcProfit( ) > 0 ; } // ok - Template-Muster !
}
© H.Neuendorf
(106)
(107)
Default Implementierungen - Mehrdeutigkeiten
IF kann von anderem IF geerbte Default-Methoden durch eigene Variante überschreiben
Implementierende Klasse kann von Oberklasse erben, die auch solche Methode enthält
Regel :
Die Oberklasse setzt sich gegen das Interface durch !
interface IFYes {
default public boolean isOk( ) { return true ; }
interface IFNo extends IFYes {
class Checker implements IFNo {
(extends kommt vor implements)
}
default public boolean isOk( ) { return false ; }
/* … */
}
}
new Checker( ).isOk( ) ; // liefert eindeutig false
class Yes {
public boolean isOk( ) { return true ; }
class Checker extends Yes implements IFNo {
}
/* … */
}
new Checker( ).isOk( ) ; // liefert true !!
IF kann von anderem IF geerbte Default-Methode abstrakt überschreiben = Implementierung entfernen
Implementierende Klasse erbt dann nur abstrakte Methode !
interface IFYes {
default public boolean isOk( ) { return true ; }
interface IFNo extends IFYes {
© H.Neuendorf
abstract public boolean isOk( ) ;
}
}
Default Implementierungen - Mehrdeutigkeiten
Klasse kann nicht zwei IFs implementieren,
die dieselbe Default-Schnittstelle enthalten !
interface IFYes {
default public boolean isOk( ) { return true ; }
}
interface IFNo {
default public boolean isOk( ) { return false ; }
}
class Checker implements IFYes, IFNo {
/* … */
}
// Fehler
Klasse kann zwei IFs implementieren, die dieselbe DefaultSchnittstelle enthalten - wenn in der Klasse die geerbten
Default-Implementierungen überschrieben werden !
interface IFYes {
default public boolean isOk( ) { return true ; }
interface IFNo {
default public boolean isOk( ) { return false ; }
}
}
class Checker implements IFYes, IFNo { // ok !
public boolean isOk( ) { return false ; } // oder:
}
© H.Neuendorf
return IFYes.super.isOK( ) ;
(108)
(109)
Default Implementierungen - super.- Zugriff
Klasse kann geerbte Default-Implementierung überschreiben …
… und trotzdem Orginal-Default-Implementierung des IFs nutzen
Vorgehen :
super.-Aufruf gegen IF
Dabei explizite Nennung des IF-Namens erforderlich !
interface IFYes {
default public boolean isOk( ) { return true ; }
}
class Checker implements IFYes {
public boolean isOK( ) { return false ; }
public boolean optimist( ) { return IFYes.super.isOK( ) ; }
}
analog zur Klassen-Vererbung …
© H.Neuendorf
Bedeutung für das Java-IF-Konzept
Java-Klasse kann beliebig viele Erweiterte IFs implementieren ⇒
Java-Klasse kann beliebig viele IFs Default-Implementierungen erben ⇒
Default-Methoden führen teilweise Mehrfach-Vererbungs-Aspekte ein (Mixin)
( Allerdings können IFs keinen Zustand einbringen - sie besitzen keine
nicht-statischen Attribute )
Abstrakte Klassen + deren Teil-Implementierungen verlieren an Bedeutung
Standardverhalten kann durch Erweiterte IFs bausteinartig angeboten werden
Strenge des IF-Konzepts und des Ist-Ein-Gedankens wird aufgeweicht
Bleibende Unterschiede zwischen Erweiterten IFs und Abstrakten Klassen AK :
AKs können nicht-public Elemente enthalten
AKs können nicht-statische Attribute enthalten
AKs können finale Methoden enthalten
AKs können Konstruktoren enthalten
© H.Neuendorf
(110)
(111)
Paketkonzept von Java
Pakete als Strukturierungsmittel
Anlegen von Paketen
Export von Paketinhalten - public versus nicht-public
Import von Paketelementen - explizit / implizit
Pakete und Filestruktur
Bedeutung von public, (package), private, protected
Design-Prinzipien für Paketstrukturen
© H.Neuendorf
(112)
Einordnung in Strukturierungsebenen
Service / App
Modul
Unit of deployment & management
Paket
Klasse
jar
Unit of state
Unit of composition & test
??
© H.Neuendorf
Java
(113)
Pakete - Packages
Bislang Strukturierung durch Klassen + Methoden
Zu feingranular
Paket
⇒ Gröberer Strukturierungsmechanismus :
: Logische + Physische Zusammenfassung in größerer Einheit =
Sammlung zusammengehöriger Klassen + Interfaces
JDK besteht aus zahlreichen Paketen – speziell : java.lang :
Standardpaket mit erforderlichen Klassen ( String, Math ...)
Nur dieses wird in jedes java-File automatisch importiert !
Arbeiten mit Paketen
1. Anlegen eigener Pakete
Schlüsselwort :
package
2. Einbinden von Paketen
Schlüsselwort :
© H.Neuendorf
import
Jedes Paket sollte
genau definierte
Zuständigkeiten +
Aufgaben besitzen !
Paket
Klasse1
KlasseN
Methoden
Attribute
Methoden
Attribute
(114)
Anlegen von Paketen
Am Anfang der Quelldatei (1.Anweisung!) :
⇒
package packageName ;
Alle Klassen / IFs dieser Datei gehören dann zu diesem einen Paket
Paket = logische Einheit - Klassen können physisch über verschiedene Dateien verteilt sein
package graphics;
package graphics;
class Circle {
class Rectangle {
…
…
}
}
Datei Rectangle.java
Datei Circle.java
Paket graphics
Circle
Rectangle
Eindeutige Paketnamen via umgekehrter Domain :
package de.dhbw-mosbach.graphics ;
© H.Neuendorf
Ohne package-Angabe gehören
alle Klassen + IF der Datei zum
namenlosem Standardpaket.
Werden im aktuellen
Arbeitsverzeichnis gesucht.
Paket = Sichtbarkeitsgrenze
Alles, was zum Paket gehört, ist außerhalb
Paket defaultmäßig nicht zugreifbar !
(115)
Paketkonzept leistet :
Deklaration öffentlicher Schnittstellen
Einschränkung von Verwendungen
Durchsetzung Geheimnisprinzip
Dokumentation Verwendungen im Code
Veröffentlichung nur durch expliziten Export !
⇒ Klassen haben nur Zugriff auf andere IFs / Klassen des gleichen Pakets !
Paketcode kann intern kooperieren - ohne dass Fremdpaket darauf Zugriff hat !
⇒ Klassen eines Pakets haben keinen Zugriff auf Klassen eines anderen Pakets !
Paket = Namensraum : Innerhalb Paket nur eindeutige Klassen-/ IF-Namen zulässig
package yourPack ;
package myPack ;
class C1 { int x;
void m( ){...} }
class C1 { int x;
class C2 { int y;
void m( ) {...} }
class C22 {
void m( ){...} }
// Klassen C1 und C2 sind lokal zu Paket myPack
C2 c = new C2( ) ; // Fehler !!
// können sich gegenseitig benutzen
// Klasse C2 hier unbekannt !!
// aber nicht von Außen (= aus anderen Paketen) ansprechbar !
© H.Neuendorf
}
(116)
Export von Paket-Inhalten - public
Gewährung Zugriff auf Interfaces, Klassen, Methoden, Attribute von Paketen
Export von Paketbestandteilen durch public -Deklaration :
"Was public ist / exportiert wird, bestimmt Paket selbst !"
Voraussetzung für Sichtbarkeit :
Zugehörige Klasse / IF selbst auch public deklariert !
Nur public Konstruktoren sind aus Fremdpaketen aufrufbar !
package myPack ;
// Auch Paket hat eine Schnittstelle !!
Wenn Klasse nur nicht-public Konstruktoren
hat, dann kann sie aus anderen Paketen nicht
instanziiert werden
Zugriff aus Fremd-Paketen :
public-Elemente von C1
public class C1 {
// C1 wird exportiert
public int x ;
// Attribut x exportiert
int y ;
public void m1( ){...}
// Methode m1( ) exportiert
void m2( ) {...}
public C1( int a ) { ... } // dieser Konstruktor exportiert
C1( ) {...}
}
class C2 { … }
// C2 von Außen nicht sichtbar !
© H.Neuendorf
Nicht-public-Elemente von C1 und
Klasse C2 jedoch verborgen !
Erlaubt:
C1 obj = new C1( 4 ) ;
Verboten: C1 obj = new C1( ) ;
Nicht-public-Elemente sind nicht
Teil des Vertrags – somit nicht als
stabil garantiert
Verwendung Paket-Inhalte :
Import in andere Pakete
(117)
Voraussetzung für Verwendung exportierter Komponenten in anderen Paketen :
Ausdrücklicher Import in verwendenden Paketen
" Was von Außen importiert wird, bestimmt das verwendende Paket selbst ! "
Sinn : Klare Festlegung, welche fremden Klassen in eigenem Paket verwendet werden !
1. Volle Paket-Qualifikation des Klassennamens
Impliziter Import
2. Import-Anweisung in Quellcode-Datei
Expliziter Import
package myPack ;
package newPack ;
public class C1 { … }
class C2 { … }
package ourPack ;
public class C1 { … }
class C2 { … }
© H.Neuendorf
// Impliziter Import :
class C10 {
myPack.C1 obj_1 = new myPack.C1( ) ;
ourPack.C1 obj_2 = new ourPack.C1( ) ;
// …
}
Gleichnamige Klassen aus
verschiedenen Paketen
durch Qualifikation
gemeinsam verwendbar
Eigene Klassen dürfen
so heißen, wie implzit
importierte Klassen
(118)
Expliziter Import
Import-Zeile in Quellcode-Datei - Varianten :
1. Import einer einzelnen Klasse :
import paketname.klassenname ;
2. Import aller Klassen eines Pakets :
import paketname.* ;
3. Statischer Import statischer Elemente einer bestimmten Klasse :
import static java.lang.Math.* ;
Regeln :
import definiert Suchpfad.
Import-Anweisungen in Quellcode-Datei direkt nach package-Anweisung !
Bindet kein Coding ein, macht
Bytecode nicht länger
Import-Anweisungen gelten nur für importierende Quell-Datei !
Kein Linking in Java
Alle Klassen müssen verschiedene Namen haben !
Verwendung * ohne Nachteile
Eigene Klassen dürfen nicht so heißen, wie explzit importierte Klassen !
→ Namenskonflikt ⇒ Impliziter Import
In Coding Klassennamen ohne Paket-Qualifikation verwendbar
package myPack ;
public class C1 { … }
class C2 { … }
© H.Neuendorf
package newPack ;
import myPack.C1 ; // expliziter Import
class C10 {
C1 obj_1 = new C1( ) ;
}
(119)
Pakete + Verzeichnisse - Regeln
Abbildung :
Klassen → Dateien
+
Pakete → Verzeichnisse
1. Eine public Klasse C muss in Datei namens C.java implementiert werden !
2. Alle Klassendateien eines Pakets P müssen in einem Verzeichnis namens P liegen !
3. Eine Datei darf beliebig viele Klassen enthalten, aber nur eine davon darf public sein !
● Name der public-Klasse C bestimmt Dateinamen: C.java
● Nicht-public-Klassen von public-Klasse intern verwendbar - aber nicht exponiert
● Innere Klassen können public sein und separat importiert werden
Paket P mit public Klassen A, B und C
⇒
Verzeichnis P mit Dateien A.java
P
A
B.java
C.java
P
B
C
A.java
set CLASSPATH=…
für .jar .zip .class
© H.Neuendorf
B.java
C.java
(120)
Pakete + Verzeichnisse – Schachtelung
Paket-Hierachie = Paket aus Paketen
Paket P3 ← Paket P1 + Paket P2
Verzeichnis P3 enthält Unterverzeichnisse P1 + P2 mit deren .java-Dateien
Keine automatische Sichtbarkeit zwischen inneren + äußeren Paketen !
Benutzung → Angabe gesamter Paketpfad bis zu referenzierten Klassen :
import P3.P2.C ;
import P3.P2.* ;
import P3.* ;
//
Import aller Klassen direkt aus P3 - nicht aber der Klassen aus P2 und P1 !
P3
P1
A.java
© H.Neuendorf
B.java
P2
C.java
D.java
N.java
Pakete + Verzeichnisse in Eclipse
(122)
Jede Klasse ist einem Package zugeordnet
Default ist das default package
= namenloses Standardpaket
Package anlegen :
Eclipse legt pro Package
Ordner mit Klassen an :
Einbinden IO in Eclipse ohne physische Kopie :
1. IO.class als ZIP- oder JAR-File zentral ablegen
2. Menü Project → Properties → Java Buid Path
3. Tab Libraries wählen
4. Button Add External JARs …
5. Auf IO-ZIP- / -JAR-File verweisen
6. OK
Referenz nachträglich entfernbar oder änderbar
© H.Neuendorf
Zugriffsrechte public
protected
package
private
public
In allen Klassen des eigenen Pakets
In allen anderen Paketen, die die Klasse importieren
protected
In Fremdklassen des selben Pakets - und deren Objekten
In Unterklassen der Klasse des selben Pakets – und deren Objekten
In Unterklassen der Klasse in anderen Paketen - jedoch nicht auf deren Objekten !
"package"
keine Angabe
In allen Klassen des eigenen Pakets - aber nicht in anderen Paketen
private
Nur in eigener Klasse selbst - nirgendwo sonst
Zugriff durch
Klasse mit Element:
© H.Neuendorf
Klasse
Unterklasse
FremdKlasse
FremdPackage
private
X
package
X
X
X
protected
X
X
X
X U-Klasse
public
X
X
X
X
(123)
Zugriffsrecht protected :
Paket P1
Mit Java-Paketkonzept verbunden
(124)
Paket P2
Oberklasse O :
protected void m( )
und
deren
Unterkl.
Unterklasse U2
import P1.* ;
Unterklasse1
Fremdklasse1
Fremdklasse2
"Fast alle" haben Zugriff auf die protected-Methode m( ) der Oberklasse P1.O
Nur die nicht erbende Fremdklasse2 hat keinen Zugriff auf protected-Elemente der
Oberklasse O aus P1
C++ : Durch protected werden
erbende Unterklassen gegenüber nicht
erbenden Klassen generell privilegiert
© H.Neuendorf
Java : Paket stellt protected-Komponenten
anderen Paketen zur Nutzung innerhalb
deren Unterklassen z.Vfg.
(125)
Zugriffsrecht protected
Situation für Unterklasse U2 aus anderem Paket P2
Zugriff auf m() nur im Coding der Unterklasse U2 - Nicht auf deren Objekten :
package P2 ;
U2 u = new U2( ) ;
import P1.O ;
u.m( ) ;
// Fehler !!
class U2 extends O {
public void test( ) { m( ) ; }
// ok!
}
Gilt auch für geerbte protected static Attribute + Methoden :
Außerhalb der Unterklasse U2 auch auf Klassennamen nicht sichtbar !
U2.statischeMethode( ) ;
// Fehler !!
Denksport :
protected = geschützt …
…vor direktem Zugriff aus fremden Paketen
© H.Neuendorf
Oberklasse hat nur einen public / private /
protected Konstruktor.
Wer kann von ihr erben ? …
Zusammenspiel Pakete + Interfaces :
Verbergen von Konkretisierungen
(126)
Facade-Pattern : Nur Schnittstellentyp direkt sichtbar - nicht konkreter Implementierungstyp
package Facade ;
package Other ;
public interface IData { int getData( ) ; }
import Facade.* ;
public class User {
package Facade ;
public static void main( String[ ] args ) {
public abstract class DataFactory { // Naming + Factory :
IData d = DataFactory.newIData( "One" ) ;
public static IData newIData( String n ) {
int n = d.getData( ) ;
// ok !
switch( n ) {
case "One" : return new DataProvider1( ) ;
// Fehler - extern nicht sichtbar :
case "Two" : return new DataProvider2( ) ;
default :
d = new DataProvider1( ) ;
return null ;
DataProvider d2 = … ;
}} }
Zugriff auf m() jedoch nur im Coding der Unterklasse 2 :
aufImplementierungen
deren Objekten : von Außen nicht sichtbar :
//Nicht
Konkrete
}
}
class DataProvider1 implements IData { public int getData( ) { return 100 ; } }
class DataProvider2 implements IData { public int getData( ) { return 200 ; } }
© H.Neuendorf
Zusammenspiel Pakete + Interfaces :
(127)
Verbergen von Konkretisierungen
Design-Idee → minimale Kopplung - maximale Flexibilität :
OCP !
Factory liefert konkrete Implementierung - jedoch nur über Interface-Typ ansprechbar
Factory liefert Referenz auf Interface-Typ
Nur gegen diesen Typ programmiert der Verwender
Konkrete Implememtierungs-Typen (Klassen) bleiben paketintern (transparent)
⇒
Jederzeit austauschbar durch andere Implementierungen (Klassen - z.B. DataProviderXY) ⇒
Verwender nicht an bestimmte implementierende Klasse gekoppelt
Völlige Entkopplung von Schnittstelle und Implementierung
Umsetzung Information Hiding + Zugangskontrolle auf Paketebene !
Public-Elemente des Pakets sind nur Interfaces und Factories, die Zugang zur eigentlichen Implementierung
vermitteln. Implementierungsklassen sind nicht öffentlich und nicht importierbar
© H.Neuendorf
(128)
Modifizierer von Klassen, Attributen, Methoden
Nicht alle Modifizierer auf alles anwendbar :
Modifizierer
Klasse
Attribut
Konstruktor
X
X
X
protected
X
X
X
private
X
X
X
X
X
X
X
public
static
X (IF)
Methode
(X innere)
final
X
abstract
X (IF)
X
strictfp
X
native
X
Speziell IFs :
IF-Methoden müssen stets
public sein
IF-Attribute müssen stets public
+ static + final sein
Sind es automatisch - auch wenn
nicht explizit vermerkt
IF selbst ist jedoch nicht
automatisch als Ganzes public !
…
Bei public- Paketklassen gilt noch viel schärfer als bei paket-internen nicht-public-Klassen :
Keine public-Attribute - sondern nur public-Zugriffsmethoden (Setter / Getter)
⇒
Nur dadurch bewahrt man die Flexibilität, die innere Datenrepräsentation der Klasse später noch
verändern / anpassen zu können …
© H.Neuendorf
(129)
Package Design Principles
Gute Struktur – keine Zyklen !
Problematische Struktur – Zyklen !
Änderung an zB MyDialogs beeinflusst nur die
Pakete MyTasks und MyApplikation – nur dort
muss entschieden werden, ob neue Version von
MyDialogs verwendet wird.
Klasse in MyDialogs benutzt nun Klassen aus MyApplikation.
Zum Compilieren & Testen von MyDialogs ist nur
Paket Windows erforderlich.
Zum Compilieren & Testen von MyTasks ist Verfügbarkeit des
gesamten Systems erforderlich.
Compilieren + Testen des Gesamtsystems kann
bottom up erfolgen – im Grunde ein Paket nach
dem anderen.
System praktisch nur noch als Ganzes testbar + compilierbar
Fazit : Klar, einfach, übersichtlich !
Compilationszeiten wachsen stark an. Jeder Entwickler muss mit selben
Releasestand aller anderen arbeiten – hoher Koordinationsaufwand.
Tool zur Struktur-Analyse : JarAnalyzer
Autor: Kirk Knoernschild
© H.Neuendorf
Testen von MyDialogs setzt Zugriff auf alle Pakete des System voraus!
Paket MyTasks ist nun von allen Paketen im System abhängig.
Aus einzelnen Paketen ist quasi ein Riesenpaket geworden, dessen
Teile nicht mehr isolierbar sind.
Fazit : Unübersichtlich, unflexibel, kaum handlebar !
⇒ Zyklus muss aufgebrochen werden !
(130)
Package Design Principles
Aufbrechen des Zyklus :
Anlegen eines neuen Pakets, von dem MyDialogs
und MyApplication nun abhängen.
Klassen, durch die die beiden Pakete zuvor direkt
verbunden wurden, kommen ins neue Paket.
Konsequenz :
Paketstrukturen müssen an neue Design-Anforderungen angepasst werden.
Wachsen des Systems + seiner Logik bedingt auch Wachsen + Wandeln der Paketstruktur.
Paketstruktur somit nicht für allemal top down fixierbar - falls Wandelbarkeit, Testbarkeit,
Erstellbarkeit des Systems nicht leiden sollen.
Weitere Definition von Stabilität :
Etwas, das nur mit großer Mühe zu
verändern ist – und sich auch
deshalb kaum ändert …
Stable-Dependencies Principles : Depend in the direction of stability
Unterscheiden zwischen Paketen, die sich oft ändern werden, sollen, dürfen bzw. leicht änderbar sind – und solchen
Paketen, die sich nicht oft ändern werden, sollen, dürfen - bzw. nur mühsam ändern lassen.
Faktoren: Größe, Komplexität, Sicherheitsrelevanz …
… insbesondere aber: Zahl der Pakete, die davon abhängen – dh Verantwortlichkeit des Pakets.
Leicht veränderliche Pakete sollen von stabilen, schwer veränderlichen Paketen abhängen – nicht umgekehrt !
… sonst würde das leicht veränderliche Paket nicht länger leicht veränderbar sein !
Bsp:
Paket Windows sollte sehr stabil sein
© H.Neuendorf
Paket MyApplication darf sich oft ändern.
(131)
Dokumentation + weitere Tools
Dokumentation von Schnittstellen - Tool javadoc
DK-Annotationen
javadoc + jar mit Eclipse-Unterstützung
Weitere JDK-Tools aus bin-Ordner
Schematischer Aufbau
java.lang / Java SE / Java EE / Java ME
© H.Neuendorf
(132)
Dokumentation von Schnittstellen
Interfaces, Klassen, Methoden, Attribute
JDK-Unterstützung → Tool :
Aufruf auf Kommandozeile :
⇒
→
Spezifikation Funktionalität + Bedeutung
javadoc *.java
javadoc [options] {filename | packageName}
HTML-Files mit Schnittstellendarstellung + Kommentaren aus Quellcode
HTML direkt in DK-Text verwendbar
/**
...
*/
Regeln für Dockommentare (DK) :
1. Jeder DK beschreibt Bezeichner dessen Deklaration unmittelbar folgt
2. Der erste Satz des DK ist Zusammenfassung = Text bis erster Punkt mit Leerschritt
3. * -Zeichen am Anfang von DK-Zeilen werden ignoriert
4. Nur DKs direkt vor Interfaces, Klassen, Methoden und Attributen werden verarbeitet
5. Wenn geerbte Methode (zB aus Interface !) keinen eigenen DK in Unterklasse hat,
erbt sie DK des Obertyps
…
© H.Neuendorf
Dokumentation : DK-Annotationen @
Java-Doc-Tags :
(133)
/**
...
*/
(u.a.)
@param
Spezikation einzelner Parameter. Erstes Wort Parametername, Rest Beschreibung
@return
Rückgabewert der Methode
@author
Autorenangabe
@version
Versionsangabe
http://download.oracle.com/javase/8/docs/technotes/tools/
@throws Ausnahmebehandlung
@deprecated
Veraltet. Compiler markiert Feld als veraltet + erzeugt Warnung
@see Querverweis auf andere Javadoc-Docu oder andere Files in doc-files Verzeichnis
Attribute und Methoden anderer Klassen mit # vor ihrem Namen.
Bsp:
{@link}
@see meineMethode(String, Object)
@see java.lang.String
@see java.lang.Math#PI
Verweise innerhalb DK-Text
Bsp:
Ändert Aufrufwert auf {@link #getValue} wenn nötig.
{@code} Erläuternde Code-Auszüge
Bsp:
© H.Neuendorf
@throws IndexOutOfBoundsException bzgl. Index. ( {@code index <0 || index>=size()} )
(135)
JavaDoc + JAR mit Eclipse
Alle Tools auch direkt mit
diversen Optionen auf
Kommandozeile ausführbar
JavaDoc
1. Ordner für die durch JavaDoc erstellten Files anlegen
2. Menü:
Project → Properties → JavaDoc Location
3. Aufruf von JavaDoc :
Project → Generate JavaDoc
Alternativ Kontextmenü :
Alle JavaDoc-Optionen einstellbar
Export → Java → Javadoc
JAR-Files
Projekt Kontextmenü :
Export → Java → JAR file
Wizzard erfagt Einstellungen bzgl. Ablageort + Inhalt
→ Runnable JAR File
(Manifest-Datei !)
Ein .jar-File mit Manifest-Eintrag für zentrale main( ) -Klasse ist direkt ausführbar
Aufruf Launcher java mit Option -jar auf Kommandozeile :
Vorteil jar :
C:\> java –jar Test.jar ↵
Organisation & Kompression
Weitergabe Projekt als ein File - statt Vielzahl von Files
Remote-Aufruf : Alle .class-Files verfügbar - nicht einzeln übers Netz zu laden
© H.Neuendorf
Weitere Tools
jps
…
im bin-Ordner der JDK-Installation
(136)
(JVM Process Status Tool)
Anzeige laufender Java-Instanzen mit ProcessID (PID)
jps
jmap
(JVM Memory Map)
Anzahl + Speicherverbrauch aller Objekte zu bestimmter PID
jmap –histo 5460
jstack (JVM Runtime Stack)
Anzeige aller laufenden Threads und deren (Warte-)Zustand
(Full Thread Dump)
jstack 5460
jstat
(JVM Statistics Monitoring Tool)
Abfrage von Performance-Statistiken
jstat –gcutil 4176
© H.Neuendorf
Aufruf auf Kommandozeile
Option –help zur Orientierung
Ausführliche Dokumentation in JDK-Doku :
http://download.oracle.com/javase/8/docs/technotes/tools/
Weitere Tools
jconsole
© H.Neuendorf
im bin-Ordner JDK
JVM Management Konsole für wählbaren Java-Prozess
(137)
Paket
java.lang
Java SE
(138)
Ausschnitt …
java.lang
Systemklassen
Wrapper
System
Runtime
RuntimePermission
Process
ProcessBuilder
Thread
ThreadGroup
ThreadLocal<T>
InheritableThreadLocal<T>
Thread.State
ClassLoader
SecurityManager
Class<T>
Compiler
Package
StackTraceElement
Boolean
Byte
Character
Character.Subset
Character.UnicodeBlock
Double
Float
Integer
Long
Short
Void
Number
© H.Neuendorf
Ausnahmebehandlung
Throwable
Exception
NullPointerException
ArrayIndexOutOfBounds-Exception
ClassCastException
ClassNotFoundException
NumberFormatException
SecurityException
Error
InternalError
NoClassDefFoundException
…
Sonstiges
Object
Math
StrictMath
String
StringBuffer
StringBuilder
…
Interfaces
Appendable
CharSequence
Cloneable
Comparable<T>
Iterable<T>
Readable
Runnable
Thread.UncaughtExceptionHandler
Wegen elementarer Bedeutung
automatisch importiert
(139)
Java SE
Java SE
Basis
java.lang
java.lang.instrument
java.lang.reflect
java.util
java.util.concurrent
java.util.jar
java.util.zip
java.util.regex
java.math
javax.management
Text
java.text
javax.swing.text
javax.swing.text.html
javax.swing.text.rtf
Grafik
java.awt
java.awt.color
java.awt.dnd
java.awt.event
java.awt.image
java.awt.font
java.awt.geom
javax.swing
javax.swing.event
Komponenten
java.beans
java.lang.reflect
... und zahlreiche weitere ...
© H.Neuendorf
Ein-/ Ausgabe
Netz/ Web
java.io
jawa.awt.print
java.awt.im
javax.imageio
javax.print
javax.sound.midi
java.net
javax.net
java.nio
java.applet
java.rmi
javax.rmi
javax.rmi.ssl
java.rmi.server
java.security
java.security.cert
javax.naming
Daten / SQL
java.sql
javax.sql
javax.sql.rowset
java.awt.datatransfer
javax.crypto
XML
javax.xml
javax.xml.parsers
javax.xml.transform
javax.xml.validation
javax.stax
javax.xml.xpath
org.w3c.dom
org.xml.sax
Java EE
Applikationsserver :
JEE
Basiert aufJava SE !
Java Standard
Servlets / JSP
javax.servlet
javax.servlet.jsp
javax.servlet.http
Client-Server-Transaktionalität
Enterprise Java Beans
javax.ejb
Grundlegende Überarbeitung durch
Version EJB 3.0 ab Java EE 5
Kein Produkt - sondern Spezifikation
Java EE-Server müssen Spezifikation
implementieren, um zertifiziert zu werden.
Mail
Message Queues
javax.mail
javax.mail.event
javax.activation
javax.jms
Oracle-Referenzimplementation GlassFish
OpenSource J2EE-Server JBoss
© H.Neuendorf
(141)
Vereinfachung der EJBImplementierung + Verteilung.
Kommunikation
javax.ressource
javax.xml.rpc
javax.xml.rpc.handler
javax.xml.rpc.server
javax.xml.rpc.soap
javax.xml.soap
Datenzugriff
javax.transaction
javax.xml.namespace
javax.xml.parsers
javax.xml.rpc
javax.xml.registry
javax.xml.transform
javax.xml.transform.dom
javax.xml.sax
management
javax.management
... und weitere ...
(142)
Java Micro Edition
Einsatz Java ME auf Geräten mit
geringer Hardwareausstattung :
Java Micro Edition
Basis
Ein-/ Ausgabe
java.lang
java.util
java.io
Frühere Java-Handys …
Spezial-APIs
javax.microedition.io
javax.microedition.lcdui
javax.microedition.rms
Basiert auf Miniversion der
virtuellen Maschine = KVM
Erweiterungen im Bereich
GUI, Netz ...
Aktuelle Bedeutung gering
aufgrund Android-Framework
Aussicht :
Nutzung für embedded devices
und Machine2MachineKommunikation im IoT
© H.Neuendorf
Java ME Wireless Toolkit
IDE incl. Emulatoren
(151)
Aufzählungstypen - Enumerations
Java5
Bedeutung Aufzählungstyp
Workarounds vor Java 5
Enum-Konzept + Minimalstruktur
Verwendungsregeln
Verschieden komplexe Ausbaustufen
Deutlich mächtiger als in C++ …
© H.Neuendorf
(152)
Aufzählungstypen - Enumerations
Aufzählung =
Zur Compilezeit bekannte, begrenzte Menge benamter Konstanten
Konventionelle Hilfskonstruktion :
Klassen mit fester Menge von Konstanten
final class Mengen { // zu simpel
public static final int Klein = 0 ;
final class Karten { // angemessen
public static final int Mittel = 1 ;
public static final int Gross = 2 ;
public static final Karten HERZ = new Karten( "Herz", 1 ) ;
public static final Karten KARO = new Karten( "Karo", 2 ) ;
private Mengen( ) { }
}
public static final Karten PIK = new Karten( "Pik", 3 ) ;
public static final Karten KREUTZ = new Karten( "Kreutz", 4 ) ;
private Karten( String s, int n ) { name = s ; rang = n ; }
private final String name ;
private final int rang ;
public String toString( ) { return name ; }
public int getRang( ) { return rang ; }
// …
}
© H.Neuendorf
Realistische Nachbildung des
enum-Konzepts durch Klassen
mit festgelegter begrenzter Zahl
konstanter, immutabler Objekte
als statische Attribute
Echte Enumerations
Java 5
(153)
class Preise {
private double stueckPreis;
Schlüsselwort
enum
private enum Mengen { Klein, Mittel, Gross ; }
Konzept im objektorientierten Kontext
public Preise( double p ) { stueckPreis = p; }
public double gesamtPreis( int zahl ) {
Java-Enums
Mengen m = Mengen.Klein ;
Besondere finale Klassen →
double preis = 0.0 ;
if( zahl > 100 )
Keine Objekte davon mit new direkt
instanziierbar oder Unterklassen ableitbar
m = Mengen.Mittel ;
if( zahl > 1000 ) m = Mengen.Gross ;
switch( m ) {
case Klein : preis = 1.0*zahl*stueckPreis ; break;
case Mittel : preis = 0.9*zahl*stueckPreis ; break;
Inhalt :
case Gross : preis = 0.8*zahl*stueckPreis ; break;
Vorgegebene Menge von Objekten
}
Selbst von java.lang.Enum abgeleitet
return preis ;
}
public static void main( String[ ] args ) {
In Klassen oder separat anlegbar …
Preise tester = new Preise( 100.0 ) ;
IO.writeln( "Preis = " + tester.gesamtPreis( 780 ) ) ;
}
}
© H.Neuendorf
(154)
Enum-Verwendungsregeln
Deklaration + Aufzählungs-Inizialisierung :
enum Mengen { Klein, Mittel, Gross ; }
Klasse Mengen angelegt - enthält als statische Elemente die immutablen Objekte
Klein, Mittel und Gross vom Enum-Typ Mengen.
Jede Enum ist eigener Namensraum
⇒ gleiche Namen in verschiedenen enums ok :
enum Mengen { Klein, Mittel, Gross ; }
enum Gebinde { Klein, Mittel, Gross ; }
1. Nicht zu anderen Typen cast-bar
2. Typsicher – nicht mit anderen Typen verwechselbar :
Mengen m = Mengen.Klein ; // OK
Mengen m = Gebinde.Klein ; // Typ-Fehler
m=2;
// Typ-Fehler
3. Typsicherheit bei Methodenaufrufen
© H.Neuendorf
Methode :
double preis( int zahl, Mengen mArt ) { … }
Aufruf :
preis( 100 , Mengen.Mittel ) ;
// ok
Aufruf :
preis( Mengen.Mittel , 100 ) ;
// Typ-Fehler
(155)
Enum-Verwendungsregeln
Enums in switch-case verwendbar
+
iterierbar :
for( Mengen m : Mengen.values( ) ) { /* … */ }
Vergleichsoperationen :
== und != zulässig
…
nicht aber <, >, <=, >=
Mengen m = Mengen.Klein ;
if( m == Mengen.Gross ) IO.writeln( "Grosse Menge" ) ;
toString( ) liefert Name des enum-Objekts :
Mengen m = Mengen.Klein ;
IO.writeln( "Inhalt: " + m ) ;
// Inhalt: Klein
IO.writeln( "Inhalt: " + Mengen.Mittel + " " + Mengen.Gross ) ; // Inhalt: Mittel Gross
Intern Ordnungsnummer gespeichert :
Mengen m = Mengen.Mittel ;
IO.writeln( "Inhalt: " + m.ordinal( ) ) ;
IO.writeln( "Inhalt: " + Mengen.Gross.ordinal( ) ) ;
Collections-Framework JDK 5 : enum-Container
© H.Neuendorf
// Inhalt: 1
// Inhalt: 2
java.util.EnumSet + EnumMap
(156)
Komplexe Enums
class Preise {
enum stellt Klasse dar
⇒
Kann Methoden, Konstruktoren,
Attribute besitzen
private double stueckPreis;
public Preise( double p ) { stueckPreis = p ; }
private enum Mengen { // Reihenfolge !!
// Konstruktoraufrufe :
Klein( 0 ) , Mittel( 100 ) , Gross( 1000 ) ;
Konstruktoren müssen private sein :
// Konstruktor :
enum-Objekte nicht von außen erzeugbar
private Mengen( int w ) { grenzWert = w ; }
Via Konstruktor können Attributwerte belegt
werden - durch Methodenaufrufe abfragbar
KonstruktorAufrufe müssen
vor KonstruktorDeklaration stehen !
// Attribut :
private final int grenzWert ;
// Methode :
Attribute sollten private final sein
public int getWert() { return grenzWert ; }
Öffentliche Attribute sind zugreifbar
public String toString( ) { return "Grenze: " + grenzWert ; }
Methoden dürfen public sein - sind auf
enum-Objekten aufrufbar
}
public double gesamtPreis( int stueck ) {
Enum-Klasse kann IFs implementieren
Jede enum ist Comparable & Serializable
Mengen m = Mengen.Klein ;
if( stueck > Mengen.Mittel.getWert( ) ) m = Mengen.Mittel ;
if( stueck > Mengen.Gross.getWert( ) ) m = Mengen.Gross ;
// …
© H.Neuendorf
Komplexe Enums
enum = Klasse
… kann für sich allein stehen !
Kann enum-Konstanten-spezifische
Methoden-Implementierungen für
abstrakte Methoden enthalten :
Jede enum-Konstante muss abstrakte
Methode in eigenem Block überschreiben
Aufzählungskonstanten enthalten dadurch
spezifische Funktionalität :
double d = Operation.PLUS.apply( 3.2, 4.5 ) ;
(157)
enum Operation {
PLUS( "+" ) {
double apply( double x, double y ) { return x + y ; }
},
MINUS( "-" ) {
double apply( double x, double y ) { return x – y ; }
},
TIMES( "*" ) {
double apply( double x, double y ) { return x * y ; }
},
DIVIDE( "/" ) {
double apply( double x, double y ) { return x / y ; }
};
private final String symbol ;
private Operation( String symbol ) { this.symbol = symbol ; }
public String toString( ) { return symbol ; }
abstract double apply( double x, double y ) ;
Kann eigene main( ) enthalten
und noch manches mehr …
public static void main( String[ ] args ) {
double x = … ;
double y = … ;
for( Operation op : Operation.values( ) )
IO.writeln( "Res: " + x + op + y + "=" + op.apply( x, y ) ) ;
}
F.Esser "Java 5 im Einsatz", S.154 ff
J.Bloch "Effective Java", S.147 ff
© H.Neuendorf
}
Herunterladen