LehrstuhlfürBioinformatik EinführungindieProgrammierungfürBioinformatiker Prof.B.Rost,Dr.L.Richter Blatt11 16.1.2017 Interfaces,GenericsundExceptions Aufgabe11.1.ErweiterunggeometrischeFiguren IndieserAufgabesollendieKlassendergeometrischenFormenvondenbeiden vorherigenAufgabenblätternerweitertwerden. • DieKlassenvomTypPrismaundGrundflaechesollenuntereinander vergleich-bargemachtwerden,indemdieKlassendasInterface Comparable<Prisma>bzw.Comparable<Grundflaeche> implementieren(https://docs.oracle.com/javase/7/docs/api/java/lang/ Comparable.html) • o PrismenwerdennachGrößedesVolumensverglichen; o GrundflaechenwerdennachGrößederFlächeverglichen. o Zur Vereinfachung ignorieren wir Abweichungen durch Gleitkommaarithmetik und gehen davon aus, dass solche nicht vorkommen. D.h. die Flächen zweier Grundflächen sind gleich, wenndieDifferenzexakt0ist.DiegleicheAnnahmetreffenwirfür dasVolumenvonPrismen. ImplementierenSiedazudieMethodepublic int compareTo(Prisma o)bzw.public int compareTo(Grundflaeche o) inderKlassePrismabzw. Grundflaeche.HaltenSiesichdabeiandieVorgabenderJava DokumentationfürdasInterfaceComparable<T>unter https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html. BeachtenSieinsbesondere,dassggf.eineNullPointerException geworfenwird. • TestenSiedieImplementierung,indemSiejeweilseineLinkedList mitGrundflaeche-ObjektenundeineLinkedListmitPrismaObjektenmittelsCollections.sort(List<T> list) sortieren.Beispiel:List<Prisma> pf = new LinkedList<Prisma>(); Collections.sort(pf); • AlszweitenSchrittsollendieMethodenistQuadratundzuQuadrat ineinInterfaceQuadrierbarausgelagertwerden,welchesnurvon solchenGrundflaechenimplementiertwerdensoll,dieauchein Quadratdarstellenkönnen. • ZuletzterstellenwireinweiteresInterfacePolygonmitnureiner Methodeint getEckenAnzahl(),welchedieZahlderineiner GrundflächeenthaltenenEckenzurückgibt.ImplementierenSiedas InterfacePolygonfüralleGrundflächen,dieeineendlicheAnzahlvon Eckenbesitzen. • TestenSieQuadrierbarundPolygon,indemSieübereine LinkedListdesTypenComparable,dieverschiedenePrisma-und Grundflaeche-Objekteenthält,iterierenundmittelsinstanceof die VerfügbarkeitderbeiderInterfacesabfragenund,fallsvorhanden,dieaus diesenInterfacesermittelbarenInformationenausgeben. • DeklarierenSiealleObjektvariablenalsfinal,wodasmöglichist. Hinweis:ZurVerwendungdergenanntenKlassenundMethodenausderJavaStandardbibliothekbenötigenSiediefolgendenImports. import java.util.Collections; import java.util.LinkedList; import java.util.List; Aufgabe11.2.Typischgenerisch Generics(zudeutsch:GenerischeProgrammierung)sindeinKonzeptinJava, welcheserstinVersion1.5hinzugekommenist.D.h.dieIdeedahinterwarnie TeilderinitialienImplementierungderSprache.Diesmerkenwiran verschiedenstenStellen.Umabwärts-kompatibelzusein,alsogenerischenJavaCodeabVersion1.5mitnichtgenerischemJava-CodevonVersionen1.4und früherzuverknüpfen,hatmansichentschieden,generi-scheDatentypenzur Compile-ZeitdurchnichtgenerischeDatentypenzuersetzen.DieserVorgang wirdi.A.als„typeerasure“(zudeutsch:Typlöschung)bezeichnet.Schauenwir unsdasaneinemBeispielan: public class A<T> { public void foo(T x) { } } public class B extends A<String> { public void foo(String x) { } } InderabgeleitetenKlasseBschränkenwirdengenerischenDatentypTaufden konkretenTypStringein.DesWeiterenüberschreibenwirdieMethodefoo,die jetzteinenStringerwartet.BetrachtenSienunfolgendesCodefragment: B b = new B(); b.foo("Hello Student!"); A a = b; a.foo(42); DasCodefragmenttyptkorrektundlässtsichohneProbleme(Warnungen/ Fehler)vomCompilerübersetzen.FührenwirdenCodeaus,dannerhaltenwir eineClassCastException.Wieso?SchauenwirunsdazumaldenCodean, dernachder„typeerasure“entsteht: public class A { public void foo(Object x) { } // T wird durch Object // automatisch vom // Compiler ersetzt } public class B extends A { // <String> wurde // entfernt public void foo(String x) { } // ... } DerCompilerhatdenTypTdurchObjectinderKlasseAersetzt.Dasbedeutet abernun,dassdieMethodefooausderKlasseBnichtmehrdieMethodefoo aus der Klasse A überschreibt, da die Methodensignaturen nicht mehr übereinstimmen.Damitnachder„typeerasure“dieMethodefoowiederinder Klasse B überschrieben wird, fügt der Compiler automatisch eine weitere Methodefoohinzu.DerCodesiehtschlussendlichwiefolgtaus: public class B extends A { // <String> wurde entfernt public void foo(String x) { } public void foo(Object x) { foo((String) x); } // diese Methode wird // automatisch vom // Compiler erstellt } Damit die Methode foo aus der Klasse A in der Klasse B wieder überschrieben wird,fügtderCompilereinesogenannte„bridge“Methodeein.Indieserwirdein Castvorgenommen.D.h.dasProgrammtyptkorrektliefertaberzurLaufzeiteine ClassCastException, da ein Integer-Objekt nicht in ein String-Objekt gecastetwerdenkann. Aufgrundder„typeerasure“könnenwirinderKlasseAauchkeineMethode foo(Object x)definieren,d.h.folgenderCodecompiliertnicht: public class A<T> { public void foo(T x) { } public void foo(Object x) { } // compiliert nicht } Denn nach der „type erasure“ würden ansonsten zwei Methoden mit der gleichenSignaturexistieren. DieoffizielleDokumentation(https://docs.oracle.com/javase/tutorial/java/ generics/erasure.html)sagtdazufolgendes: GenericswereintroducedtotheJavalanguagetoprovidetightertypechecksat compile time and to support generic programming. To implement generics, the Javacompilerappliestypeerasureto: • ReplacealltypeparametersingenerictypeswiththeirboundsorObject ifthetypeparametersareunbounded.Theproducedbytecode,therefore, containsonlyordinaryclasses,interfaces,andmethods. • Inserttypecastsifnecessarytopreservetypesafety. • Generatebridgemethodstopreservepolymorphisminextendedgeneric types. Typeerasureensuresthatnonewclassesarecreatedforparameterizedtypes; consequently,genericsincurnoruntimeoverhead. Wir wissen nun, dass zur Kompilezeit alle generischen Datentypen ersetzt werden.GibtesweitereFallstricke? A<Integer> a1 = new A<Integer>(); A<String> a2 = new A<String>(); if (a1 instanceof A && a2 instanceof A) System.out.println("instanceof: yes"); if (a1.getClass() == a2.getClass()) System.out.println("getClass(): yes"); InbeidenFällenevaluierendieAusdrückezutrue. Diesistinsofernunschön, dasswirzurLaufzeitnichtmehrzwischenverschiedenengenerischenTypenvon der selben Klasse unterscheiden können. Das fällt uns besonderns negativ auf, wennwireine equals-Methodeimplementierenwollen.Hierkönnenwirzum BeispielnichtmehrzwischeneinerListevonStringsundeinerListevonIntegern unterscheiden. D.h. LinkedList<String> und LinkedList<Integer> haben zur Laufzeit den gleichen Typ, nämlich LinkedList. Im Javasprech werden solche Typen auch rawtypes(https://docs.oracle.com/javase/tutorial/ java/generics/rawTypes.html)genannt. Quintessenz: All diese Probleme sind Folgen der Designentscheidung, dass AbwärtskompatibilitätsowichtigfürdieSprachdesignerwar. Aufgabe11.3.Exceptions BetrachtenSiefolgendenAusschnittausderKlassenhierarchievonExceptionsin JavaundfolgendeJava-ImplementierungderKlasseExceptionTest: import java.io.*; public class ExceptionTest { public static void main (String[] args) { try { // ... } catch (EOFException e) { System.out.println("EOFException"); } catch (IOException e) { System.out.println("IOException"); } catch (Exception e) { System.out.println("Exception"); } System.out.println("ENDE"); } } } 1) An der durch „...“ gekennzeichneten Stelle im try-Block stehe ein Programmstück, durch das Exceptions vom Typ EOFException, IOException oder FileNotFoundException geworfen werden können. Was wird bei Ausführung der main-Methode ausgegeben, falls dabei im try-Block i. alsersteseineAusnahmevomTypEOFExceptiongeworfen wird, ii. alsersteseineAusnahmevomTypFileNotFoundException geworfenwird,oder iii. garkeineAusnahmegeworfenwird? 2) WaswirdbeiAusführungdermain-Methodeausgegeben,fallsdabeiim try-BlockalsersteAusnahmeeineDivisiondurch0auftritt? Aufgabe11.4.ResistenteMengen VerwendenSiefürdieseAufgabenurdieerlaubtenJava-Methoden. ImplementierenSieeineunveränderlicheDatenstruktur,diedasVerhalteneiner Mengeabbildet.Unveränderlichbedeutet,dassjedeMembervariablefinalsein muss. Möchte man also ein Element e der Menge E hinzufügen, so darf/kann nicht die Menge E selber verändert werden, sondern es muss ein neues Objekt E’ erzeugt werden, welches das neue Element e beinhaltet sowie alle alten Element der Menge E, i.e. E’ = E ∪ {e}. Um die Elemente einer Menge zu repräsentieren, verwenden wir eine Liste. Da wir gefordert hatten, dass die Menge unveränderlich sein muss, fordern wir auch, dass die Liste un- veränderlichseinmuss.D.h.alleMembervariablenderListemüssenfinalsein. Für die Listenimplementierung dürfen Sie sich an Ihren eigenen Listenimplementierungen sowie an den Lösungsvorschlägen aus vorherigen Aufgaben orientieren. Die Menge selber soll Elemente von einem generischen TypTenthaltenundsomitnatürlichauchdieListe. ImFolgendenseiseinSet-ObjektvoneinemgenerischenTypT: • DerparameterloseKonstruktorerstellteinObjektwelcheseineleere Mengerepräsentiert • DieMethodes.add(T e)gibteinSet-Objektzurück,dassdasElemente enthältsowiealleElemente,dieinsenthaltensind.IstdasElemente schoninsenthalten,dannsollszurückgegebenwerden.Istegleich null,dannsolleineNullPointerExceptiongeworfenwerden. • DieMethodes.remove(T e)gibteinSet-Objektzurück,dasalle Elementvonsenthält,außerdemElemente.Istegleichnull,dannsoll eineNullPointerExceptiongeworfenwerden. • DieMethodes.contains(T e)gibttruezurückwenndasElemente inderMengeenthaltenistundandernfallsfalse. • DieMethodesizegibtdieKardinalitätderMengezurück. • DieMethodes.equals(Object o)erfülltdieüblichenEigenschaften, diederJava-Standardfordert.DesWeiterenwirdtruezurückgegeben, wenndieMengensundodiegleichenElementeenthält.Andernfallswird falsezurückgegeben.BeachtenSiedieüblichenEigenschafteneiner Mengewiez.B.e∈E⇒E=E∪{e}odere∉E⇒E=E\{e}. • DieMethodes.toString()gibteinenStringderForm{x1,...,xn} zurück,wenndieMengesdieElementexi,1≤i≤nenthältundndie KardinalitätderMengeist.DerzurückgegebenStringenthältalsoeine String-RepräsentationallerenthaltenerElementederMenge.