Folgende Nachricht wurde in der JDC
Tech Tips No. 5 versendet. Lies den Text und implementiere das Beispielprogramm.
Teste es an der Klasse
java.math.BigDecimal
.
Wofür kann man die Technik verwenden? Schreibe ein Programm, welches zu einer geladenen
Klasse eine Klassenbeschreibung liefert, wie sie im Quellcode vorliegen könnte.
Zu der Beschreibung gehören also Konstruktor, Variablen und Methoden.
Eine Firma möchte Komponenten implementieren und auch anderen Anbietern die Möglichkeit geben, Komponenten zu entwickeln. Da das Einfügen neuer Komponenten möglichst einfach sein soll, sollen sie in ein Verzeichnis gestellt werden. Wird die Software neu gestartet, so soll sie automatisch die neuen Komponenten erkennen und einbinden.
Möchte man von einer gegebenen Klasse die Vererbungshierarchie auflisten, so ist dies kein großes Problem. (Wieso?) Es gibt aber auch Situationen im Leben eines Programmierers, in denen er alle direkten erbenden Klassen erfahren möchte. Wo liegen aber dort die Schwierigkeiten?
Allerdings lassen sich in einem absoluten Sonderfall Unterklassen herausfinden. Warum funktioniert für welchen Spezialfall das folgende Programmstück:
Class<?> superC = Point2D.class; Class[] refs = superC.getClasses(); for ( int i = 0; i < refs.length; i++ ) if ( superC != refs[i] && superC.isAssignableFrom( refs[i] ) ) System.out.println( refs[i] + " is subclass of " + superC );
Teste das Programmsegment.
Wir wollen als konkretes Beispiel einen Taschenrechner einsetzen, der um mathematische Funktionen erweitert werden soll. Definiere eine Schnittstelle für das Problem. Das kann etwa so aussehen:
interface MathOp { String getName(); double op( double d1, double d2 ); }
Dann kann eine konkrete implementierende Klasse, die eine Funktion implementiert, Folgendes schreiben:
class Sin implements MathOp { String getName() { return "sin"; } double op( double d1, double d2 ) { return Math.sin( d1 ); } }
op()
ist allerdings noch nicht so schön. Warum?
Wie kann man sie verbessern? Schreibe eine Testklasse, die die mathematische Funktion überprüft. Das kann etwas so aussehen:
public static void main( String args[] ) { Sin sin = new Sin(); // Parameter aufbauen System.out.println( sin.op( /* Parameter einsetzen */ ) ); }
Kopiere die eigenen Klassen in die Freigabe TNProjekte
auf dem Referenten-PC.
Kopiere anschließend alle mathematischen Klassen in das eigene Verzeichnis.
Man kann mit der Methode list()
von einem
java.io.File
eine Auflistung aller Dateien im Verzeichnis erhalten.
IMathOp
,
aufgelistet werden? Kann eine Funktion endXXX()
aus String
vielleicht helfen? Man kann Klassen mit forName()
nachladen, wenn man den Klassennamen
kennt. Mit newInstance()
kann man dann ein Exemplar der Klasse erzeugen.
Ein Beispiel:
Class clazz = Class.forName("java.util.Date"); Object o = clazz.newInstance();
Lade alle mathematischen Klassen im Verzeichnis und erzeuge ein Objekt von ihnen
mit Hilfe der newInstance()
-Methode. Mit toString()
kann
man erkennen, ob alles gut ging. Fange die entsprechenden Ausnahmen ab.
Übergebe dazu alle gefundenen Klassen von list()
der Methode
klasseLaden()
, die das Objekt vom Typ MathOp
zurückliefert.
Wenn eine Annotation ein Modifizierer wie public
, static
oder final
ist, welche Konsequenz hat das?
Mit EJB 3 ist es möglich, ohne XML-Konfigurationsdateien (aka Deployment-Deskriptor) Enterprise JavaBeans zu schreiben. Ein Beispiel:
// import javax.ejb.Stateless; // import javax.ejb.Remote; @Remote @Stateless public class CalculatorBean // implements Calculator { public int add( int x, int y ) { return x + y; } public int subtract( int x, int y ) { return x - y; } }
Definiere die dafür nötigen Annotations-Typen.
Der Annotations-Typ @Stateless
soll um Elemente
erweitert werden.
name
vom Typ
String
. Es soll standardmäßig auf "" stehen.TransactionManagementType
(nicht im Inneren von @Stateless
), mit den
Elementen CONTAINER
und
BEAN
. @Stateless
soll ein Element transactionManagement
bekommen,
welches standardmäßig auf CONTAINER
steht.Erzeuge eine neue Annotation @Stateful
. Kopiere
die Elemente aus @Stateless
, denn es sind die selben!
@Target
ist bei
@Stateless
/@Stateful
sinnvoll?@Remove
, welches
nur auf Methoden angewendet werden darf. Es soll ein Element
boolean retainIfException() default false;
definieren.@Retention
gesetzt ist.Teste, ob der Typ CalculatorBean
eine Annotation
trägt. Erfrage, welche Annotationen mit den Klassen verbunden sind.
Was macht die folgende Klasse? Bringe das Beispiel mit einem neuen Annotations-Typ
@Test
zum Laufen.
import java.lang.reflect.Method; public class TestRunner { static void executeTests( String className ) { try { executeTests( Class.forName(className) ); } catch ( ClassNotFoundException e ) { e.printStackTrace(); } } static void executeTests( Class clazz ) { try { Object testObject = clazz.newInstance(); Method[] methods = testObject.getClass().getDeclaredMethods(); for ( Method amethod : methods ) { Test testAnnotation = amethod.getAnnotation( Test.class ); if ( testAnnotation != null ) { System.out.print( testAnnotation.value() + " : " ); String result = invoke( amethod, testObject ); System.out.println( result ); } } } catch ( Exception e ) { e.printStackTrace(); } } static String invoke( Method m, Object o ) { try { m.invoke( o, (Object[]) null ); return "passed"; } catch ( Exception x ) { return "failed"; } } public static void main( String[] args ) { executeTests( "ClassToTest" ); // executeTests( ClassToTest.class ); } }