Praktikum aus Softwareentwicklung 2, Stunde 3 Lehrziele/Inhalt 1. 2. 3. 4. 5. Ereignisbehandlung, Auslösen von Ereignissen JScrollPane Java2D Reflection Eclipse-Tastenkürzel Ereignisbehandlung, Auslösen von Ereignissen Löst ein Objekt Ereignisse aus, muss es eine Methode implementieren mit der sich ein Listener anmelden und eine Methode mit der er sich wieder abmelden kann. Die Methode zum Anmelden muss add<ListenerName> und die Methode zum Abmelden remove<ListenerName> heißen. Unterstützt eine Klasse zum Beispiel MouseListener, dann müssen die Methoden addMouseListener und removeMouseListener heißen. Außerdem muss es die Methode fire<EventName> geben mit der die Events verschickt werden können. Listener-Interface Für jedes mögliche Ereignis muss ein Listener-Interface existieren. Listener-Interfaces können beliebige Methoden enthalten. Zum Beispiel hat MouseListener die Methoden mouseClicked, mouseEntered, mouseExited, mousePressed und mouseReleased. Die Methoden müssen als Parameter ein Event-Object haben, zB: im MouseListener ein MouseEvent. Listener-Interfaces müssen von dem Marker-Interface java.util.EventListener erben. Ereignis-Objekte Für jedes Ereignis muss ein Ereignis-Objekt erstellt und an alle Listener verschickt werden. Jeder Listener erhält dasselbe Ereignis-Objekt, daher müssen Ereignis-Objekte unveränderbar (immutable) sein. Ereignis-Objekte in Java erben von der Klasse EventObject. Diese Klasse implementiert Serializable, daher müssen nicht-serialisierbare Felder als transient markiert werden. Push oder Pull? Ereignisse sind häufig mit Änderungen in einem Datenmodell verbunden. Soll man die Änderung gleich im Event übertragen (Push), oder soll der Interessierte das Datenmodell selbst inspizieren (Pull)? Eine klare Antwort darauf gibt es nicht. Man muss von Fall zu Fall unterscheiden was die bessere Alternative ist. Push ist für den Event-Empfänger häufig bequemer, während Pull bei Änderungen des Datenmodells stabil ist. Bei der Entscheidung Push oder Pull sollte man auch an die Serialisierbarkeit der gepushten Objekte denken. JScrollPane Komponenten die grösser sind als der zur Verfügung stehende Platz werden rechts unten abgeschnitten. Mit scrollen hat man eine Möglichkeit solche Komponenten doch darzustellen. In Java übernimmt JScrollPane diese Aufgabe. Damit eine Komponente scrollbar wird muss sie in eine © Markus Löberbauer 2010 Seite 7 JScrollPane gekapselt werden, zB: new JScrollPane(new JVeryBigComponent()). Und die Komponente muss ihre Größe für die JScrollPane berechnen können. Dazu kann man die Methode getPreferredSize überschreiben. Oder wenn man mehr Einfluss auf die JScrollPane haben will das Interface Scrollable implementieren. Java2D Seit Java 1.2 übernimmt Java2D die Zeichenaufgaben in Java, es ist aber kompatibel zu dem RenderVerhalten von Java 1.1. Java2D ermöglicht zB: Abstraktion von physikalischen Pixeln, Transformationen, Effekte und Antialiasing. Der Einstiegspunkt in Java2D ist die Klasse Graphics2D. Seit Java 1.2 ist jedes Graphics-Objekt ein Graphics2D-Objekt und kann daher gecaset werden. Graphics wird nur aus Kompatibilitäts-Gründen in der Schnittstelle übergeben. Mit Graphics2D kann man die Strichart ändern (setStroke); Transformationen wie zB Skalieren, Rotieren anbringen (getTransform); das Füllmuster festlegen (setPaint); den Clipping-Bereich setzen; und die Komposition bestimmen (setComposite). Unter Komposition versteht man: wie vorhandene Farben auf der Zeichenfläche mit der aktuellen Zeichenoperation verbunden werden sollen. Beispielsweise kann man das neue Element darüber oder darunter legen oder verschiedene Schnitte machen, siehe java.awt.AlphaComposite. Benötigt man ein spezielles Clipping oder spezielle Transformationen sollte man mit einer Kopie des übergebenen Graphics-Objekts arbeiten. Eine Kopie kann mit der Methode Graphics.create anlegen werden. Schrift Text wird mit der Methode Graphics.drawString ausgegeben. Die Schriftart (java.awt.Font) kann man mit Graphics.setFont setzen. Zeichnet man eine komplexe Komponente die Text enthält, dann braucht man häufig die Länge des auszugebenden Texts. Damit man umbrechen oder beispielsweise abkürzen kann. Solche Maßzahlen über Text kann man über die Klasse FontMetrics erhalten. Objekte der Klasse FontMetrics kann man über Graphics.getFontMetrics abfragen. Bilder Bilder werden mit der Methode Graphics.drawImage gezeichnet. Bilder kann man in Java als BufferedImage erzeugen, und über ein Graphics2D-Objekt beschreiben. Ein Graphics2D-Objekt erhält man über die Methoden getGraphics und createGraphics. Die beiden Methoden liefern das gleiche Objekt, allerdings liefert getGraphics das Objekt aus Kompatibilitätsgründen als Graphics-Objekt. Häufig ist es interessant Bilder aus Dateien zu laden oder in Dateien zu speichern. Laden von Bildern erfolgt mit der Methode ImageIO.read, Speichern mit ImageIO.write. Reflection Mit Reflection kann der Programmierer zur Laufzeit programmatisch auf Typinformationen zugreifen, zB: welche Felder, Methoden und Konstruktoren hat eine Klasse. Über Reflection kann man aber auch Objekte erzeugen, Methoden aufrufen, Felder lesen und schreiben. Mit dynamischen Proxies (java.lang.reflect.Proxy) kann man zur Laufzeit Objekte erzeugen die gegebene Interfaces implementieren. Die benötigten Klassen sind in den Packages java.lang.reflect und java.lang. © Markus Löberbauer 2010 Seite 8 Einsatzgebiete Die JavaVM benutzt Typinformation beispielsweise zur Speicherbereinigung (garbage collection). In der Java Klassenbibliothek wird sie benutzt um Klassen nachzuladen (java.util.ServiceLoader). Programmier können sie benutzen um Werkzeuge zu bauen, zB: Klassen-Browser, Test-Frameworks (zB: JUnit) und Debugger. Verwendet man Reflection sollte man sich aber der Nachteile bewusst sein zB: Geschwindigkeitsverlust durch Klassen-Analyse zur Laufzeit; und Sicherheitsprobleme, über Reflection kann auch auf private Elemente einer Klasse zugegriffen werden. Class Der Einstiegspunkt in Reflection ist die Klasse Class. Ein Objekt dieser Klasse erhält man über: a. b. c. d. <Klassenname>.class, zB: ArrayList.class <Wrapper für einen primitiven Datentyp>.TYPE, zB Integer.TYPE entspricht int.class ein Objekt mit der Methode getClass() Class.forName("Klassenname als String"), zB: Class.forName("java.util.ArrayList") Über das Class-Objekt kann man die Elemente in einer Klasse abfragen: Methoden (getMethods, getDeclaredMethods), Felder (getFields, getDeclaredFields), Konstruktoren (getConstructors, getDeclaredConstructors), innere Klassen (getClasses, getDeclaredClasses). Die Methoden mit den Namen get<…> liefer alle public-Elemente inklusive der geerbten. Die Methoden mit den Namen getDeclared<…> liefern alle Elemente einer Klasse, unabhängig von ihrer Sichtbarkeit, aber keine geerbten. Von allen Elementen kann man mit getDeclaringClass auf die deklarierende Klasse zugreifen. Elemente einer Klasse Reflektierte Elemente kann man weiter untersuchen, zB: Methoden auf Rückgabetyp (getReturnType) und Parametertypen (getParameterTypes); oder man kann damit arbeiten: Constructor, anlegen neuer Objekte (newInstance) Method, aufrufen einer Methode (invoke) Field, lesen und schreiben (get, set; und für primitive Datentypen get<Type>, set<Type>, zB: getInt, setInt) Auf Private Elemente einer Klasse kann über Reflection zugegriffen werden, dazu muss man setAccessible(true) setzen. Ob ein Element zugreifbar ist kann über isAccessible abgefragt werden. Eclipse-Tastenkürzel 1. Ctrl-Shift-F: Code formatieren. 2. Ctrl-Shift-O: Fehlende Import-Statements einfügen, überflüssige entfernen. 3. Ctrl-Shift-R: Umbenennen einer Klasse, Methode, … © Markus Löberbauer 2010 Seite 9