Laboration 18 Undantag, Class Avsikten med denna laboration är att du ska träna på att använda undantag, använda några av metoderna i klassen Class och skriva några enkla rekursiva metoder. Sist i laborationen är det facit över uppgifterna. Konsultera detta när du är färdig med uppgiften. Given klass: InputPanel Klassen InputPanel är given och kan användas vid lösning av uppgifterna 1och 3. Ett objekt av klassen kan t.ex. användas tillsammans med metoden JOptionPane.showMessageDialog. Vid anrop till metoden visas då panelen: InputPanel panel = new InputPanel("Ange din längd"); JOptionPane.showMessageDialog(null, panel, "Titel", JOptionPane.PLAIN_MESSAGE); String res = panel.getinput(); De två första raderna ger dialogen till höger. För att påbörja inmatningen måste användaren klicka i inmatningsfönstret. Sedan går det bra att skriva in ett värde. När användaren klickat på OK kan innehållet i inmatningsfönstret läsas med getinput-metoden. Klassen innehåller ett par set- och get-metoder: setmessage(string) setinput(string) getinput() : String Ändrar innehållet i meddelanderaden (ovan Ange din längd ) Ändrar innehållet i inmatningsfönstret Returnerar innehållet i inmatningsfönstret public class InputPanel extends JPanel { private JLabel lblmessage; private JTextField tfinput = new JTextField(); public InputPanel(String str) { setlayout(new BorderLayout()); lblmessage = new JLabel(str); add(lblmessage,borderlayout.center); add(tfinput,borderlayout.south); public String getinput() { return tfinput.gettext(); public void setinput(string defaultinput) { tfinput.settext(defaultinput); public void setmessage(string message) { lblmessage.settext(message); DA129A, Programmering 1 1
Uppgift 1 Inmatning av heltal I klassen Input finns metoden readint vilken du har använt när användaren ska mata in ett heltal. Metoden fungerar på så sätt att om användaren matar in något som kan översättas till ett heltal så returneras detta värde. Om så inte är fallet returneras 0. Nu ska du skriva en egen version av readint-metoden, public static int readint(string str) { : Den ska fungera på så sätt att om användaren matar in något som kan översättas till ett heltal så returneras detta värde. Om så inte är fallet ska undantaget NumberFormatException kastas av metoden (unchecked - behöver ej deklareras i undantagslista). Lägg märke till att metoden är en klass-metod, dvs innehåller static i deklarationen. För att översätta en sträng till ett heltal kan du använda metoden Integer.parseInt(String). Metoden returnerar en int om strängen går att göra om till ett heltal. Om så inte är fallet så kastas undantaget NumberformatExeption. Testkod int tal; tal = Uppgift1.readInt("Mata in ett heltal"); System.out.println("Du matade in: " + tal); catch(numberformatexception e) { System.out.println(e); Några exempel på körresultat: Inmatningen Ulrik ger utskriften java.lang.numberformatexception: For input string: "Ulrik" Inmatningen 37498 ger utskriften Du matade in: 37498 Inmatningen 17.94 ger utskriften java.lang.numberformatexception: For input string: "17.94" 2
Uppgift 2a Inmatning av decimaltal Nu ska du skriva metoden readdouble vilken ska fungerar lite annorlunda än readint i Uppgift 1. Användaren matar in ett värde och klickar på OK. Om användare matar in ett värde som inte går att översätta till en double och sedan klickar på OK så ska dialogen visa sig på nytt och användaren ska få ett meddelande om vad som är fel. Exempel Inmatning: 1,84 (det ska vara decimalpunkt) Inmatning: ganska lång Även om användaren klicka på Stäng-knappen uppe till höger ska dialogen visas på nytt (inklusive FELAKTIG INPUT! ) Uppgift 2b Snyggare felutskrift (extra) Det går bra att rendera html i vissa komponenter, bla i JButton och JLabel. Det innebär t.ex. att det är enkelt att visa text med olika färg och storlek i samma JLabel-komponent. Detta går bra att använda för att generera en snyggare utskrift i inmatningsdialogen ovan. Exempel String message = "Ange din längd (m)"; InputPanel panel = new InputPanel(message); panel.setmessage("<html>" + message + "<br><font color='red' size='2'>" + "FELAKTIG INPUT</font></html>"); JOptionPane.showMessageDialog(null, panel, "Titel", JOptionPane.PLAIN_MESSAGE); Resulat Exception-objekt har en metod, getmessage(), vilken returnerar en sträng med felmeddelandet. Det är alltså möjligt att generera en felutskrift liknande denna vid felaktig inmatning: For input string: 1,84 är resultatet av anrop till getmessage-metoden. 3
Uppgift 3 Klassen NotInIntervalException är given: public class NotInIntervalException extends Exception { public NotInIntervalException() { public NotInIntervalException(String str) { super(str); Du ska skriva metoden readintervall vilket ska låta användaren mata in heltal i ett bestämt intervall. Metoden ska deklareras så här: public static int readintervall(int min, int max) {... Om min > max så ska variablerna byta värde så att min blir minst. Metoden ska kasta följande undantag: Om användaren anger ett heltal som inte är i intvallet [min, max] ska undantaget NotInIntervalException kastas med ett felmeddelande typ: 22 utanför intervallet [30, 45] Om användaren matar in något som inte går att översätta till ett heltal ska undantaget NumberFormatError kastas (sker automatiskt om du använder metoden Integer.parseInt) Testkod int tal; tal = Uppgift3.readInterval(30,45); System.out.println(tal); catch(notinintervalexception e) { System.out.println(e.getMessage()); catch(numberformatexception e) { System.out.println(e.getMessage()); Körresulat Dialogen till höger visar sig. Beroende på inmatning får man olika utskrifter. Metoden kan använda Uppgift1.readInt(String) för inmatningen. Inmatning Utskrift 42 42 22 22 utanför intervallet [30, 45] 45 45 46 46 utanför intervallet [30, 45] 5.8 For input string: "5.8" 4
Uppgift 4 I denna uppgift ska du skriva ett litet program med vars hjälp man kan studera variabler, konstruktorer och metoder i en klass. Du ska använda dig av klasserna Class, Field, Constructor och Method. Programmet består av två fönster: UserGUI i vilken man kan skriva in namnet på en klass. Lägg märke till att man måste ta med samtliga paket. Sedan väljer användaren ett av alternativen Variabler / Konstruktorer / Metoder beroende på vad användaren vill se i klassen. ListViewer vilken visar ett antal strängar. En sträng per rad. Meningen är att viewer i detta programmet visar alla publika variabler / konstruktorer / metoder som deklareras i eller ärvs till en klass. (koden för klassen given nedan) package laboration5; import java.awt.*; import javax.swing.*; public class ListViewer extends JPanel { private DefaultListModel model = new DefaultListModel(); public ListViewer() { setlayout(new BorderLayout()); add(new JScrollPane(new JList(model))); public void updatecontent(object[] obj) { model.clear(); for(int i=0; i<obj.length; i++) model.addelement(obj[i].tostring()); 5
En tänkbar design är att du använder tre klasser. UserGUI för interaktion med användaren, Viewer för att visa innehåll i aktuell klass och Controller för att styra flödet i programmet. Denna design innebär att: UserGUI-objektet meddelar Controller-objektet vad användaren matat in för klass (en sträng) och vad användaren vill se i klassen Controller-objektet skapar ett Class-objekt och anropar någon av metoderna * getfields() * getconstructors() * getmethods() I samtliga fall kan resultatet av anropet lagras i en Object-array. Resultatet används som argument till metoden updatecontent i ListViewer-klassen. Class cls = Class.forName(klassnamn); // hämta variabler/ konstruktorer / metoder och meddela viewern. catch(classnotfoundexception e) { // hantera felaktig inmatning ListViewer-klassen är given. Det går ganska lätt att fylla en JList-komponent med statiskt innehåll ( konstruktorn JList(Object[]) ). Men vi måste kunna ändra innehållet i JListkomponenten. För att detta ska vara möjligt krävs bl.a. en klass som använder dynamisk lagring och som implementerar gränssnittet ListModel. Klassen DefaultListModel är en sådan klass. Om man ändrar innehållet i DefaultListModel-objektet så kommer ändringarna att visa sig i JList-komponenten. Ett problem med UserGUI är att det är lätt hänt att mata in ett felaktigt klassnamn. Om så är fallet kastas ett ClassNotFoundException av forname-metoden och detta undantag måste fångas (checked exception). Du bör lägga till kod i felhanteraren så att felmeddelandet skrivs ut i viewern. 6
Uppgift 1 public static int readint(string message) { InputPanel panel = new InputPanel(message); JOptionPane.showMessageDialog(null, panel, "Inmatning av heltal", JOptionPane.PLAIN_MESSAGE); return Integer.parseInt(panel.getInput()); Uppgift 2 public static double readdouble(string message) { double res = 0; boolean ok = false; InputPanel panel = new InputPanel(message); while(!ok) { JOptionPane.showMessageDialog(null, panel, "Inmatning av decimaltal", JOptionPane.PLAIN_MESSAGE); res = Double.parseDouble(panel.getInput()); ok = true; catch(numberformatexception e) { // panel.setmessage("<html>" + message + // "<br><font color='red' size='2'>" + // "FELAKTIG INPUT: " + e.getmessage() + "</font></html>"); panel.setmessage("felaktig INPUT! " + message); return res; Uppgift 3 public static int readinterval(int min, int max) throws NotInIntervalException { int res, temp; if(min>max) { temp = min; min = max; max = temp; res = readint("mata in ett tal i intervallet: [" + min + ", " + max + "]"); if(res<min res>max) throw new NotInIntervalException(res + " utanför intervallet [" + min + ", " + max +"]"); return res; 7
Uppgift 4 package laboration18; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class UserGUI { private JPanel panel = new JPanel(new BorderLayout()); private JButton btnvariabler = new JButton("Variabler"); private JButton btnkonstruktorer = new JButton("Konstruktorer"); private JButton btnmetoder = new JButton("Metoder"); private JTextField tfclassname = new JTextField(); private Controller controller; /** Creates a new instance of UserGUI */ public UserGUI(Controller controller) { JPanel south = new JPanel(new GridLayout(1,3)); this.controller = controller; panel.setlayout(new BorderLayout()); btnvariabler.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { UserGUI.this.controller.update(tfClassName.getText(), Controller.FIELDS); ); btnkonstruktorer.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { UserGUI.this.controller.update(tfClassName.getText(), Controller.CONSTRUCTORS); ); btnmetoder.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { UserGUI.this.controller.update(tfClassName.getText(), Controller.METHODS); ); south.add(btnvariabler); south.add(btnkonstruktorer); south.add(btnmetoder); panel.add(tfclassname, BorderLayout.CENTER); panel.add(south, BorderLayout.SOUTH); public JPanel getpanel() { return panel; 8
package laboration18; import javax.swing.*; import java.awt.*; public class Controller { public final static int FIELDS = 1, CONSTRUCTORS = 2, METHODS = 3; private ListViewer viewer = new ListViewer(); private UserGUI usergui = new UserGUI(this); public Controller() { UserGUI usergui = new UserGUI(this); viewerframe(); userframe(); private void viewerframe() { JFrame frame = new JFrame("User control"); frame.setbounds(50,50,500,80); frame.getcontentpane().add(usergui.getpanel()); frame.setdefaultcloseoperation(jframe.exit_on_close); frame.setvisible(true); private void userframe() { JFrame frame = new JFrame("Viewer"); frame.setbounds(50,200,500,300); frame.getcontentpane().add(viewer, BorderLayout.CENTER); frame.setdefaultcloseoperation(jframe.exit_on_close); frame.setvisible(true); public void update(string classname, int type) { Class cls = Class.forName(className); switch(type) { case FIELDS : viewer.updatecontent(cls.getfields()); break; case CONSTRUCTORS : viewer.updatecontent(cls.getconstructors()); break; case METHODS : viewer.updatecontent(cls.getmethods()); break; catch(classnotfoundexception e) { viewer.updatecontent(new Object[]{e.toString()); public static void main(string[] args) { Controller controller = new Controller(); 9