enum
: Datatype for en liste av verdierAlle instruksjoner i et java-program — unntatt
import
-instruksjoner og reine deklarasjoner
— må ligge inne i en eller annen metode. Og metodene må i sin tur
ligge inne i en eller annen klasse. Vi har sett at alle applikasjonsprogrammer
må ha en klasse som inneholder en main
-metode.
Mange av programeksemplene våre har ikke bestått av annet enn nettopp dette:
en klasse med en main
-metode som har inneholdt
alle instruksjonene til programmet.
Det er klart at når det blir litt størrelse på programmet, så blir dette
uoversiktlig og gir små muligheter til å dele opp programmet i naturlige
enheter. Det normale for et program er at
main
-metoden inneholder bare én eller
noen få instruksjoner, som i sin tur setter i gang kall på andre metoder som
hver har sin bestemte oppgave å utføre. I kapittel 3 hadde vi noen eksempler
på dette, der vi brukte klasser til å definere forskjellige typer kursbokobjekter.
I main
-metoden opprettet vi objekter av de definerte typene og
brukte dem til å gjøre kall på sine metoder.
Klasser og metoder er de to hovedtypene av moduler som et javaprogram er bygget opp av. Vi skal nå gå nærmere inn på hvordan metoder og klasser defineres og brukes.
I kapittel 3 ble en metode beskrevet som en navngitt samling av instruksjoner som til sammen skal utføre en bestemt oppgave. Det ble påpekt at det er viktig å ha klart for seg skillet mellom det å definere en metode og det å gjøre kall på den (kalle den opp). Det ble gitt en generell beskrivelse av hvordan en metodedefinisjon ser ut, se Kapittel 3, eksempel 1. Metodedefinisjonen inneholder de instruksjonene metoden skal utføre dersom den blir kalt opp. Men instruksjonene blir ikke utført uten at metoden blir kalt opp. Og de blir utført hver gang metoden blir kalt opp.
Metodene i java er av to hovedtyper:
Vi skal etter tur se nærmere på hver av de to typene.
Dette er metoder for å utføre generelle oppgaver som ikke har noe med et bestemt objekt å gjøre.
Eksempler:
Klassemetoder er kjennetegnet ved at definisjonen av metoden inneholder nøkkelordet
static
.
Derfor kalles disse metodene også for static
-metoder.
For eksempel så starter definisjonen av klassemetoden showInputDialog
slik:
public static String showInputDialog(Object message) ...
I metodedefinisjonen til en static
-metode er det bare tillatt å referere
til de av klassens datafelt som det også står nøkkelordet static
foran.
For øvrig er det ingen formelle forskjeller mellom definisjonen av en klassemetode og en
objektmetode. Derimot så er det forskjell på hvilken måte vi kaller opp de to typene metoder.
Kall på en klassemetode: Vi må bruke klassenavnet (for den klassen som inneholder metodedefinisjonen) sammen med navnet til metoden, det vil si et uttrykk med denne struktur:
Klassenavn.metodenavn(...)
Istedenfor de tre prikkene skal det enten være én eller flere parametre, dersom metoden er definert til å ha noen slike, eller ikke noe. Men parentesene er nødvendig å ha. Det er de som indikerer at det som står foran dem er navnet på en metode.
Det er også tillatt å gjøre kall på en klassemetode ved at vi istedenfor klassenavnet bruker et objekt av vedkommende klassetype. Men for å markere at det er en klassemetode, blir det anbefalt at man isteden bruker klassenavnet. Dersom vi gjør kall på en klassemetode inni den klassen der metoden er definert, er det tilstrekkelig å bruke metodenavnet (i tillegg til metodeparentesene og eventuelle aktuelle parametre).
Java-eksempler
String input = JOptionPane.showInputDialog( "Skriv antall:" ); // kall på showInputDialog definert i klassen JOptionPane int tall = Integer.parseInt( input ); // kall på parseInt definert i klassen Integer
Klassen Character
, som også er en av klassene i javas klassebibliotek,
kan brukes til å konvertere ("pakke inn") primitive char
-verdier (altså verdier
av type tegn) til å bli objekter. Klassen inneholder en del klassemetoder for manipulering med
tegn, blant annet metoden toUpperCase
som returnerer tilsvarende stor
bokstav av den bokstaven som er parameter til metoden. Som aktuell parameter er det mest
aktuelt å bruke en char
-variabel som på forhånd har fått en eller annen verdi.
Et eksempel på bruk av metoden kan vi derfor skissere slik:
char bokstav = ...; //variabelen bokstav blir tildelt et //eller annet tegn som verdi char storBokstav = Character.toUpperCase(bokstav);
Klassen Character
inneholder selvsagt også en metode som foretar konvertering
fra stor til liten bokstav. Det er klassemetoden toLowerCase
. En oversikt over alle
metodene i klassen Character
kan du finne på nettadressen
http://download.oracle.com/javase/8/docs/api/java/lang/Character.html.
Klassen Math
inneholder klassemetoder for de vanlige matematiske funksjonene. En
oversikt over metodene kan du finne på nettadressen
http://download.oracle.com/javase/8/docs/api/java/lang/Math.html.
En litt spesiell klassemetode er main
-metoden for et program. Kallet på den blir
foretatt av Javas kjøresystem.
Eventuelle parametre i en metodedefinisjon kalles formelle parametre. De angis med datatype og parameternavn på samme måte som når vi deklarerer variable. Er det flere av dem, bruker vi komma mellom hvert slikt par av datatype og parameternavn. Parametre som vi bruker i kallet på en metode, kalles aktuelle parametre, eller argumenter. Da er det bare parametrene vi bruker, ikke datatype. Men aktuelle parametre må alltid stemme overens med metodedefinisjonen når det gjelder antall, datatype og riktig rekkefølge. En aktuell parameter kan enten være en variabel (som må være av riktig datatype og på forhånd være tilordnet en verdi) eller en konkret verdi (literal) av vedkommende datatype. Det er ingen ting i veien for at en aktuell parameter kan ha et annet navn enn den tilsvarende formelle parameteren, men datatypen må stemme overens. Virkemåten er at verdien til den aktuelle parameteren blir tilordnet som verdi til den tilsvarende formelle parameteren når metoden blir utført. Det som her er sagt om parametre gjelder både for klassemetoder og objektmetoder.
Dette er metoder som brukes til å utføre operasjoner på bestemte objekter, for eksempel endre noen av objektets data. Det er slike metoder vi skal ta for oss i resten av kapitlet. Hvordan et kall på en objektmetode ser ut, er avhengig av om det skjer innenfor eller utenfor klassen der metoden er definert:
metodenavn( < aktuelle parametre > );
Eksempel: Se Kapittel 3, Merknad 2, der
det blir gjort kall på metoden getKursnavn
innenfor den klassen
metoden er definert i. Metoden er uten parametre.
Kursboktest3
,
der det gjøres kall på metodene setKursnavn
og
visTittel
, definert i klassen Kursbok3
.
Kallene gjøres ved hjelp av objektet protokoll
.I det følgende skal vi ta for oss flere eksempler på definisjon og kall av objektmetoder.
Metoden antSekunder
definert i klassen
Tidsberegner
nedenfor har tre parametre, alle av type
int
.
Metoden beregner og returnerer det antall sekunder som de tre parametrene
angir, når den første tolkes som et antall timer, den andre som et antall
minutter, og den tredje som et antall sekunder.
En metode kan ha et hvilket som helst antall parametre, og parametrene kan være
av forskjellige og av hvilke som helst datatyper.
public class Tidsberegner { public int antSekunder( int timer, int min, int sek ) { return timer * 3600 + min * 60 + sek; } }
Klassen Tidstester
gjengitt nedenfor oppretter et objekt av
type Tidsberegner
og gjør kall på dens metode
antSekunder
. Som aktuelle parametre i kallet brukes det verdier
som leses inn fra brukeren. Ved kallet må vi passe på at metoden vil tolke første
parameter som et antall timer, andre parameter som et antall
minutter, og tredje parameter som et antall sekunder. Vi må derfor
passe på at de innleste verdiene settes inn som aktuelle parametre i riktig
rekkefølge. Verdien som metoden antSekunder
returnerer, blir
returnert til kallstedet for metoden. For å kunne bruke den returnerte verdien i
utskriften som følger etterpå, må vi fange opp verdien i variabelen sekunder
.
Innlesing og beregning av tider blir utført gjentatte ganger, inntil
brukeren svarer med nei på et spørsmål om hun eller han ønsker å beregne flere tider.
Til dette brukes en Ja-nei-dialogboks, slik det ble forklart i
Kapittel 3, Eksempel 4. Legg merke til hvordan
den returnerte verdien fra dialogboksen blir brukt til å gi verdi til en logisk
variabel, som i sin tur blir brukt som betingelse for at en
do-while
-løkke skal gjentas.
1 import javax.swing.JOptionPane; 2 3 public class Tidstester 4 { 5 public static void main( String[] args ) 6 { 7 boolean flere = true; // styrevariabel for do-while-løkke 8 Tidsberegner klokke = new Tidsberegner(); 9 10 do 11 { 12 String timetall = JOptionPane.showInputDialog( 13 "Skriv antall timer (heltall):" ); 14 String minuttall = JOptionPane.showInputDialog( 15 "Skriv antall minutter (heltall):" ); 16 String sekundtall = JOptionPane.showInputDialog( 17 "Skriv antall sekunder (heltall):" ); 18 int timer = Integer.parseInt( timetall ); 19 int min = Integer.parseInt( minuttall ); 20 int sek = Integer.parseInt( sekundtall ); 21 int sekunder = klokke.antSekunder( timer, min, sek ); 22 String utskrift = timetall + " timer, " + minuttall + 23 " minutter, " + sekundtall + " sekunder = " + 24 sekunder + " sekunder."; 25 JOptionPane.showMessageDialog( 26 null, utskrift, "Beregning av tid i sekunder", 27 JOptionPane.PLAIN_MESSAGE ); 28 int svar = JOptionPane.showOptionDialog( 29 null, "Vil du beregne flere tider?", 30 "Tidsberegning", JOptionPane.YES_NO_OPTION, 31 JOptionPane.QUESTION_MESSAGE, null, null, null ); 32 flere = (svar == JOptionPane.YES_OPTION); 33 } while ( flere ); 34 } 35 }
Følgende bilde viser en dialogboks fra en kjøring av programmet.
Prøv ut programmet ved å kjøre det selv! Du kan laste ned de to filene
som definerer klassene beskrevet ovenfor ved å klikke på følgende linker:
Tidsberegner.java
,
Tidstester.java
.
Variable som vi deklarerer inni en metode, vil eksistere inni denne metoden mens den blir utført, men ellers vil variablene ikke eksistere eller være tilgjengelige. Variablene vil ikke være tilgjengelige (det vil si at vi ikke kan ikke referere til dem) utenfor metoden. De kalles derfor lokale variable. NB! Lokale variable blir ikke automatisk tildelt noen startverdi! Vi må derfor selv huske på å initialisere dem.
NB! Metodens parametre er også lokale variable! De blir tildelt som startverdi de verdiene som de aktuelle parametre har når metoden blir kalt opp. Inni metoden står vi fritt i å bruke parametrene på samme måte som vi gjør med andre lokale variable, for eksempel tilordne dem nye verdier.
Obs: Det er ikke tillatt å definere en metode inni en annen metode.
Når en metode returnerer en verdi, så blir denne returnert til det stedet der metoden ble kalt opp. Programkontrollen går også tilbake dit når metoden er ferdig med sin utførelse.
Definer en klasse Sirkel
som skal representere en sirkel.
Klassen skal ha et datafelt av type double
for sirkelens radius.
Dette skal tildeles verdi via en konstruktørparameter. Konstruktøren skal
sjekke om den radien den mottar som parameter er >= 0.0
.
Dersom dette ikke er tilfelle, skal sirkelens radius settes lik 0.0
.
Klassen skal ha en get
-metode som returnerer sirkelens radius.
Klassen skal i tillegg ha følgende metoder, skissert i pseudo-kode:
public double diameter() { < Beregner og returnerer sirkelens diameter. > } public double omkrets() { < Beregner og returnerer sirkelens omkrets. > } public double areal() { < Beregner og returnerer sirkelens areal. > }
(Fra matematikk vet vi at når sirkelens radius er r
, så
er diameteren lik 2 * r
, omkretsen er lik 2 * Π * r
,
og arealet lik Π * r * r
.)
For konstanten Π kan du bruke konstanten Math.PI
som
finnes i javas klassebibliotek. (Den ligger i pakken java.lang
og
blir derfor automatisk importert.)
Skriv en klasse Sirkeltest
som gir et testprogram for
Sirkel
-klassen. Programmet skal fra brukeren lese inn radius til
en sirkel og bruke denne verdien som konstruktørparameter ved opprettelsen av
et Sirkel
-objekt. Programmet skal skrive ut data for sirkelen,
tilsvarende som vist i følgende dialogboks (du kan ha en annen verdi for
sirkelens radius):
De utskrevne tallverdiene skal formateres til to desimaler. I kapittel 2 kan du finne beskrivelse av innlesing av desimaltall. I kapittel 4 er formatering av utskrift beskrevet.
I kapittel 4, eksempel 4 er det et program som gjentatte ganger leser inn et antall sekunder og skriver ut dette konvertert til timer, minutter og sekunder. Programmet avsluttes når brukeren skriver et negativt tall. Du får nå som oppgave å omarbeide dette programmet på følgende måte:
Skriv en klasse Tidskonverterer
ved at du supplerer
følgende skisse med det som er skrevet i pseudo-kode:
public class Tidskonverterer { public String konvertertTid( int antSekunder ) { < Returnerer en tekst som uttrykker det mottatte antall sekunder i timer, minutter og sekunder. > } }
Eksempel: Dersom metoden kalles opp med en aktuell parameter lik 7132, kan den returnerte teksten fra metoden være slik:
1 time, 58 minutter, 52 sekunder
Skriv en klasse Tidskonvertering
som gir et testprogram
for klassen Tidskonverterer
. Programmet skal opprette et
Tidskonverterer
-objekt. Programmet skal gjentatte ganger kunne
lese inn et antall sekunder fra brukeren. Det innleste antallet skal konverteres
til timer, minutter og sekunder, ved at programmet gjør kall på
Tidskonverterer
-objektets metode konvertertTid
, med
den innleste verdi som aktuell parameter. Resultatet av konverteringen skal
skrives ut i en dialogboks. Programmet skal avsluttes når det leses inn en
negativ verdi for antall sekunder.
Med slumptall mener vi tall som blir generert helt tilfeldig, tilsvarende de tallene vi får ved å kaste med en ideell terning, det vil si en terning som er fullkomment symmetrisk slik at ingen av sidene har tendens til å bli vist oftere enn en annen. I datasammenheng er slumptall ofte til nytte. Det kan for eksempel være når vi skal programmere spill der slumptall inngår (som for eksempel simulering av terningkast), eller ved utprøving av programmer. Det er derfor nyttig å ha et verktøy, det vil si et hjelpeprogram som genererer slumptall for oss. Det er ikke mulig å lage noe program som produserer "ekte" slumptall. Uansett hvordan vi går fram, må vi bruke en eller annen bestemt regel for å få generert tallene. Som regel er det snakk om mer eller mindre kompliserte matematiske formler. Det er imidlertid utviklet slike formler som produserer tall som i svært stor grad simulerer ekte slumptall. (Det kan vi få verifisert ved å produsere store antall slike tall og konstatere at de fordeler seg jevnt på de forskjellige intervallene som de hentes fra.) Siden det likevel ikke er ekte slumptall, er det riktig å kalle dem pseudo-slumptall (det vil si tall som opptrer som om de skulle vært ekte slumptall).
I javas klassebibliotek finnes det flere ressurser som kan brukes til å
generere slumptall. Én mulighet er å bruke static
-metoden
random()
definert i klassen Math
.
Klassen ligger i pakken
java.lang
og blir derfor automatisk importert
til alle javaprogrammer. Ved å skrive instruksjonen
Math.random();
får vi returnert i form av en double
-verdi et
tilfeldig tall fra det halvåpne intervallet [0.0, 1.0>
.
Denne verdien kan vi skalere og eventuelt forskyve, slik at vi får verdien
i det intervall vi ønsker. Vi kan også strippe den for desimaler, ved at vi
konverterer den til type int
.
En annen mulighet er å opprette et objekt av type Random
.
Klassen Random
må vi da importere til programmet fra pakken
java.util
. I denne klassen finnes det flere forskjellige
next
-metoder for å få generert en ny slumpverdi av forskjellige
datatyper, se
http://download.oracle.com/javase/8/docs/api/java/util/Random.html
for en oversikt over mulighetene. Det vi oftest har bruk for, er å få
generert hele tall på slump. Skriver vi
Random generator = new Random(); int slumpverdi = generator.nextInt();
så får vi generert en tilfeldig heltallsverdi hentet fra hele det mulige
intervallet for slike verdier, positiv eller negativ. Som regel ønsker vi at
verdiene skal ligge innenfor et bestemt intervall, for eksempel fra 1 til 6
(begge grenser inkludert) i tilfelle vi ønsker å simulere kast med en vanlig
terning. For å få til dette, kan vi gå fram på følgende måte, ved å bruke
objektet generator
som er opprettet ovenfor:
int terningkast = 1 + generator.nextInt( 6 );
Metodekallet generator.nextInt( 6 )
vil returnere et
tilfeldig tall blant verdiene 0, 1, 2, 3, 4, 5. Ved å addere 1 får vi forskjøvet
dette til å bli et av tallene 1, 2, 3, 4, 5, 6. Mer generelt ser vi at parameteren
som brukes i kallet på nextInt
bestemmer hvor mange forskjellige
muligheter vi kan få på returverdien fra denne. Dersom dette er lik verdien til
variabelen antMuligheter
, så vil den returnerte verdien være en
av verdiene 0, 1, 2, ... , (antMuligheter - 1)
. Dersom vi ønsker
at den laveste av verdiene skal være lik verdien til variabelen min
,
så kan vi oppnå det ved å addere den til returverdien. For eksempel vil vi ved
å skrive
int verdi = min + generator.nextInt( antMuligheter );
få en verdi lik et av tallene min, (min + 1), (min + 2), ...,
(min + antMuligheter - 1)
.
Dersom vi for eksempel ønsker en tilfeldig heltallsverdi fra intervallet
[m, n]
, begge grenser medregnet, der m <= n
,
så vil min
være lik m
, og
antMuligheter
vil være lik n - m + 1
. Vi kan derfor
skrive
int verdi = m + generator.nextInt( n - m + 1 );
Det er ikke tillatt å bruke en negativ parameter i kallet på metoden
nextInt
. Dersom vi prøver oss med det, vil det bli skrevet ut
en feilmelding. (Det kastes ut en Exception
av type
IllegalArgumentException
.)
Javas slumptallsgenerator er laget slik at vi får forskjellige serier av
slumptallsverdier hver gang vi bruker generatoren, selv om verdiene er
programmert til å tilhøre samme intervall. I noen situasjoner kan vi ønske å
få samme serie av slumptall om igjen ved hver kjøring. Det kan vi oppnå dersom vi
oppretter Random
-objektet på følgende måte:
Random generator = new Random( beregningsgrunnlag );
der beregningsgrunnlag
er en long
-verdi som vi
selv bestemmer.
For å teste ut javas slumptallsgenerator, skal vi lage et program som simulerer kast med en terning 6000 ganger og teller opp hvor mange ganger terningen gir 6. Dersom slumptallsgeneratoren genererer de seks mulige terningverdiene med omtrent like stor sannsynlighet, burde antall seksere bli i nærheten av 1000.
Vi skal først definere en klasse som representerer en terning. Den trenger ikke ha mer enn disse egenskapene: Terningen må til enhver tid ha en verdi (lik antall øyne på den side som snur opp på en virkelig terning), vi må kunne se hva denne verdien er lik, altså kunne lese den ut, og vi må kunne foreta kast med terningen for å gi den en tilfeldig ny verdi (som kan slumpe til å være lik den forrige verdien). Operasjonene må kunne foretas om igjen og om igjen så mange ganger vi vil. Følgende klasse oppfyller disse kravene:
1 import java.util.Random; 2 3 public class Terning 4 { 5 private int verdi; 6 private Random generator = new Random(); 7 8 public Terning() 9 { 10 kast(); 11 } 12 13 public void kast() 14 { 15 verdi = 1 + generator.nextInt( 6 ); 16 } 17 18 public int getVerdi() 19 { 20 return verdi; 21 } 22 }
I klassens konstruktør gjør vi et kall på kast
-metoden,
slik at terningen får en tilfeldig startverdi.
I simuleringsprogrammet skal vi bruke et Terning
-objekt til
å simulere kastene. Vi trenger også en tellevariabel for å registrere hvor
mange seksere vi får. For hvert kast må vi sjekke om vi har fått en sekser, og i
så fall skal vi øke tellevariabelen med én. Programmet kan se ut som i
følgende klasse.
1 import javax.swing.JOptionPane; 2 3 public class Seksertest 4 { 5 public static void main( String[] args ) 6 { 7 Terning terningen = new Terning(); 8 int antKast = 6000; 9 int antSeksere = 0; // har ikke fått noen sekser ennå! 10 11 for ( int i = 1; i <= antKast; i++ ) 12 { 13 terningen.kast(); 14 int verdi = terningen.getVerdi(); 15 if ( verdi == 6 ) 16 antSeksere++; // ny sekser registrert 17 } 18 19 String utskrift = "Antall seksere på " + antKast + " terningkast: " 20 + antSeksere; 21 JOptionPane.showMessageDialog( null, utskrift, "Terningtest", 22 JOptionPane.PLAIN_MESSAGE ); 23 } 24 }
Siden terningverdiene beregnes som slumptall, vil programmet gi forskjellig resultat hver gang vi kjører det (se merknad ovenfor). En kjøring av programmet ga følgende resultat:
Du kan få lastet ned filene til ditt eget område ved å klikke på følgende
linker:
Terning.java
,
Seksertest.java
.
Lag et program som simulerer kast med en terning 100 ganger. Verdiene
skal skrives ut i et tekstområde med 10 verdier per linje, med et mellomrom
(blankt tegn) mellom hver verdi. Tekstområdet skal til slutt vises i en
meldingsboks. I programmet kan du bruke samme Terning
-klassen som
i eksemplet ovenfor (men programmet som bruker terningen må lages annerledes).
Bruk av tekstområde vil du finne beskrivelse av i
kapittel 5. For å få skrevet ut 10 verdier
på hver linje, kan du gå fram på følgende måte: Du bruker en styrevariabel
n
i løkka som foretar simuleringen, og lar denne gå fra 1 til 100.
Hver gang styrevariabelen er delelig med 10,
(n % 10) == 0
,
tilføyer du et linjeskifttegn "\n"
til tekstområdet.
Generelt vil noe som er navngitt (en variabel, en metode, etc.) være tilgjengelig i den blokka der navnet er deklarert og i alle innenforliggende blokker. En variabel vil eksistere (i memory) i løpet av den tida den blokka som den er deklarert i blir utført (eksekvert). Den vil da eksistere fra det tidspunkt den blir deklarert og til den blokka den ligger i er ferdig utført.
Som regel bør vi deklarere variable på så lokalt nivå som mulig: Vi oppretter dem og lar dem dermed være tilgjengelige på det nivå der vi har behov for dem, ikke andre steder. For eksempel deklarerer vi ikke på klassenivå en variabel som vi bare har behov for inne i en av klassens metoder. Den deklarerer vi isteden lokalt inne i vedkommende metode, eventuelt inne i en av metodens underliggende blokker.
Dersom vi har deklarert et datafelt (en instansvariabel) i en klasse og så bruker samme navn på en lokal variabel i en av klassens metoder, så vil vi miste muligheten til å referere til dette datafeltet mens vi er inne i vedkommende metode. Derfor bør vi vanligvis unngå å bruke datafeltnavn som navn på lokale variable. Å gjøre det, kan også lett føre til forvirring og feil som det er vanskelig å oppdage.
I en og samme klasse er det tillatt å definere metoder som har samme
navn, men forskjellig parameterliste. Returverditypen kan da være lik eller
forskjellig. Vi sier i slike tilfelle at vi overloader metodene. Det finnes ingen
god oversettelse til norsk av det engelske ordet overload. En metodes navn,
samt typeliste for parametre (datatype og rekkefølge) blir, som vi tidligere
har vært inne på, kalt
metodens signatur. Det er den som (sammen med klassen den er definert i)
identifiserer metoden. Returverditype og parameternavn blir ikke regnet med i
signaturen. Vi ser at overloadete metoder får forskjellige signaturer. Derfor
kan de holdes fra hverandre. (Det er ikke tillatt å ha identisk signatur og
forskjellig returverditype.) Antall parametre kan være forskjellig for
overloadete metoder. Overloading er nyttig for å kunne gjøre kall på samme
metode med input-data av forskjellig type eller omfang. For eksempel har vi
tidligere gjort kall på metoden
JOptionPane.showMessageDialog
med eller uten
input-data for tekst i tittellinje.
I eksempel 1 ovenfor definerte vi klassen
Tidsberegner
som inneholdt metoden antSekunder
for
beregning av hvor mange sekunder et antall timer, minutter og sekunder ville
utgjøre. Metoden hadde parametre for disse antallene. I noen tilfeller kan vi
tenke oss at sekundene ikke spiller noen rolle: Vi kjenner et tidsrom i antall
timer og minutter, og er interessert i hvor mange sekunder dette blir. Det kan
også tenkes at det bare er timene vi er interessert i: Vi lurer på hvor mange
sekunder et antall timer vil utgjøre. For å kunne håndtere disse situasjonene,
kan vi utvide klassen vår med overloadete versjoner av metoden
antSekunder
, slik at vi kan gjøre kall på den som passer oss i
det enkelte av de nevnte tilfellene. Klassen kan da se ut som følger:
public class Tidsberegner { public int antSekunder( int timer, int min, int sek ) { return timer * 3600 + min * 60 + sek; } public int antSekunder( int timer, int min ) { return timer * 3600 + min * 60; } public int antSekunder( int timer ) { return timer * 3600; } }
Fra og med javaversjon 1.5, ble det i javaspråket innført en mulighet for at en metode kan ha et varierende antall argumenter av samme datatype. Syntaksen for dette er som antydet her:
returverditype metodenavn(datatype ... parameternavn)
{
< metodeinnhold >
}
Det som signaliserer at det aktuelle parameterantallet kan være varierende,
er altså de tre prikkene ...
mellom datatype og parameternavn.
Ved kall på metoden kan vi bruke så mange paramtre vi vil av den spesifiserte typen,
eventuelt ingen. Vi lister da opp parametrene med komma mellom hver parameter.
I tillegg til den uspesifiserte lista av parametre kan metoden ha andre parametre,
så mange vi vil. Men det er et krav at lista med varierende antall parametre må stå
til slutt, og det kan bare være én slik liste.
Reint teknisk virker dette på den måten at parametrene som kan variere i antall
blir lagt inn i en tabell når metoden blir utført. Navnet på vedkommende parameter
er derfor i virkeligheten en tabellvariabel av den datatype som er spesifisert.
(Dette lærer vi om i kapittel 7)
Inni metoden kan denne tabellen gjennomløpes for prosessering. Til dette kan vi
bruke en for
-løkke. Siden det
i dette tilfelle vil være hele tabellen som skal gjennomløpes, kan vi bruke en
utvidet for
-løkke. Selvsagt
kunne vi også brukt en vanlig for
-løkke.
Det er sannsynligvis ikke så ofte du vil ha bruk for å programmere med denne type parameterliste, men igjen så er det nyttig å kjenne til syntaksen, slik at du kan forstå betydningen i tilfelle du treffer på den i kode som andre har skrevet.
For å lage et eksempel på definisjon av metode med varierende antall parametre,
skal vi gå tilbake til eksempel 3 i kapittel 6.
Det ble der laget tre forskjellige versjoner av metoden antSekunder
for konvertering til sekunder av et tidsspenn som kunne angis enten i bare timer,
i timer og minutter, eller i både timer, minutter og sekunder. De tre metodene kunne
vi isteden ha skrevet som én enkelt metode på følgende måte:
public int antSekunder(int ... tid) { int total = 0; //antallet som skal returneres int faktor = 3600; for (int antall : tid) { total += antall * faktor; faktor /= 60; } return total; }
Med denne definisjonen kunne vi skrevet metodekallene akkurat på samme måte som vi ville ha gjort med den opprinnelige versjonen der vi hadde tre overloadete metoder.
I en del tilfeller bruker vi i programmer bestemte tallverdier som ikke skal endres i løpet av programmet, de er med andre ord det vi pleier å betegne som konstanter. Dersom vi i slike tilfeller bruker konkrete tall, medfører det et par problemer: For det første er det, når vi leser programkoden, ikke så lett å vite hva tallene egentlig står for, vi kunne ønsket oss at de var utstyrt med en merkelapp som fortalte nettopp det. For det andre kan det lett oppstå feil i tilfelle vi ved en oppdatering av programmet skal endre deres verdier: Vi kan lett overse steder der dette skulle vært gjort. På den annen side kan vi komme i skade for å endre deres verdier når det ikke skulle gjøres, fordi vi trodde at det tallet vi endret ikke var vedkommende konstant.
For å unngå disse problemene, bør vi i slike tilfeller opprette navngitte
konstanter. Det gjør vi på samme måte som når vi deklarerer og initialiserer
variable, men med den forskjell at vi tilføyer nøkkelordene static
og final
. Det første av disse indikerer at konstanten tilhører
vedkommende klasse, og ikke noe objekt av klassen. (En nærmere omtale av dette
finner du i kapittel 8.) Nøkkelordet final
indikerer at det blir
gitt verdi én gang for alle, det er ikke tillatt å endre verdien i
løpet av programmet. Det er jo nettopp dette som ligger i begrepet 'konstant'!
public static final int KAPASITET = 100; public static final double RENTEFOT = 2.75;
Det er vanlig praksis å bruke bare store bokstaver i navnene på slike
konstanter, eventuelt supplert med lav bindestrek, som for eksempel i navnet
NEDRE_GRENSE
. Dette har den fordel at det i programkode er lett å
se hva som er konstanter og hva som ikke er det. Konstantens navn vil i programmet
være et navn på den verdien som den er tilordnet. Vanligvis blir konstanter
definert på klassenivå. Dersom vi skal referere til dem utenfor klassen, må
vi prefikse navnet med klassenavnet. For eksempel er det i klassen
Math
(i pakken java.lang
) definert konstanter for
tallene pi (forholdet mellom omkretsen og diameteren i en sirkel) og e
(grunntallet i det naturlige logaritmesystemet). Disse kan vi referere til som
Math.PI
og Math.E
. Klassen Math
inneholder
for øvrig metoder for alle de vanligste matematiske funksjonene. En oversikt kan
du finne på nettadressen
http://download.oracle.com/javase/8/docs/api/java/lang/Math.html
enum
: Datatype for en liste av verdierI enkelte tilfeller kunne vi ønske oss en datatype som begrenset seg til
en kort liste av navngitte verdier, for eksempel for de fem virkedagene i uka.
Fra og med java-versjon 5.0 (det vil si 1.5) er det mulig å definere slike
opplistingstyper ved å bruke nøkkelordet enum
. For eksemplet med
de fem virkedagene kunne vi skrevet definisjonen på denne måten:
enum Virkedag { MANDAG, TIRSDAG, ONSDAG, TORSDAG, FREDAG };
En verdi av en slik opplistingstype kan vi se på som en navngitt konstant. Vi skriver den derfor, som vist i eksemplet, ved å bruke bare store bokstaver. Det blir regnet som dårlig stil ikke å følge denne regelen.
Slik som vi kan for datatyper ellers, kan vi også for slike opplistingstyper deklarere variable av vedkommende type. Når vi har definert typen ovenfor, kan vi for eksempel skrive
Virkedag møtedag, fridag;
Verdiene som vi kan tilordne en slik variabel er imidlertid begrenset til
dem som er inneholdt i definisjonen av datatypen, samt verdien null
,
som indikerer at variabelen ikke har noen "virkelig" verdi. De tillatte
"virkelige" verdiene må vi imidlertid prefikse med navnet på datatypen, som i
følgende eksempel:
møtedag = Virkedag.TIRSDAG;
Som for alle andre datatyper, kan vi også initialisere samtidig som vi deklarerer. Vi kunne for eksempel skrevet
Virkedag langdag = Virkedag.TORSDAG;
Den definerte datatypen er brukt i følgende lille programeksempel.
Programmet
Virkedagsdemo
er gjengitt nedenfor. Når programmet kjøres, får vi en utskrift som vist på
følgende bilde:
1 import javax.swing.JOptionPane; 2 3 public class Virkedagsdemo 4 { 5 enum Virkedag { MANDAG, TIRSDAG, ONSDAG, TORSDAG, FREDAG }; 6 7 public static void main( String[] args ) 8 { 9 Virkedag startdag = Virkedag.MANDAG; 10 Virkedag sluttdag = Virkedag.FREDAG; 11 12 String utskrift = "Arbeidsuka starter på " + startdag; 13 utskrift += "\nSiste arbeidsdag er " + sluttdag; 14 JOptionPane.showMessageDialog( null, utskrift, "Arbeidsuke", 15 JOptionPane.PLAIN_MESSAGE ); 16 } 17 }
Legg merke til at definisjonen av opplistingstypen er plassert utenfor
main
-metoden og at det heller ikke er opprettet noe objekt av
klassen Virkedagsdemo
for å bruke typen. Dette indikerer at en slik opplistingstype
implisitt tilhører den klasse den er definert i, det vil si brukes som om den
var tilordnet nøkkelordet static
. Det er for øvrig på tilsvarende
sted vi pleier å definere navngitte konstanter. (Men for disse bruker vi
vanligvis nøkkelordet static
i tillegg til final
.)
På grunnlag av programeksemplet ovenfor og den utskriften det gir, kan vi
få inntrykk av at verdiene til en opplistingstype, slik som
Virkedag.MANDAG
ovenfor, er String
-verdier. Det er
imidlertid ikke tilfelle. Hvordan de egentlig er implementert,
trenger vi ikke bry oss noe om. Vi trenger ikke vite det for å kunne bruke
datatypene. Det vi trenger å vite, er for eksempel at Virkedag.TORSDAG
og Virkedag.FREDAG
er forskjellige verdier og at vi kan sjekke
på likhet ved å bruke ==
. Av eksemplet ovenfor ser vi imidlertid
at vi i dette tilfelle kunne brukt datatype String
istedenfor
den definerte opplistingstypen. Vi kunne skrevet
String startdag = "MANDAG";
istedenfor
Virkedag startdag = Virkedag.MANDAG;
Men når vi bruker String
, gir det mulighet for å tilordne
vedkommende variabel meningsløse verdier, som i dette tilfelle
"JULAFTEN"
eller
"Hurra!"
,
uten at det medfører noen feilmelding. Bruker vi en opplistingstype, er det bare
de verdiene som er listet opp som godtas. Vi får en feilmelding straks vi prøver
oss med noe annet.
En opplistingstype er i virkeligheten en klasse og dens verdier er objekter av denne klassen. Det har også som følge at det er noen metoder som kan kalles opp ved bruk av slike opplistingsverdier (som altså er en type objekter). Nedenfor står en oversikt over de viktigste av disse metodene.
public boolean equals( <en verdi av en opplistingstype> )
Kan brukes ved sammenlikning som alternativ til ==
. For
opplistingstyper er
førsteverdi.equals( andreverdi )
ekvivalent med
førsteverdi == andreverdi
public String toString()
Returnerer strengrepresentasjon av kallende verdi. Blir ofte kalt opp automatisk. Det er for eksempel tilfelle i instruksjonen
String utskrift = "Arbeidsuka starter på " + startdag;
som er brukt i programeksemplet ovenfor. Skriver vi for eksempel
(med datatypen Virkedag
definert som ovenfor)
Virkedag.ONSDAG.toString()
så får vi returnert "ONSDAG"
.
public int ordinal()
Returnerer posisjonen til den kallende verdien i lista som definerer
datatypen. Første posisjon er 0. Med datatypen Virkedag
definert
som ovenfor vil for eksempel
Virkedag.TORSDAG.ordinal()
returnere 3.
public int compareTo( <en verdi av en opplistingstype> )
Returnerer en negativ verdi dersom kallende objekt står foran argumentet
i lista som definerer datatypen, returnerer 0 i tilfelle kallende objekt er lik
argumentet, og returnerer en positiv verdi dersom kallende objekt kommer etter
argumentet i lista som definerer datatypen. Med datatypen Virkedag
definert som ovenfor, vil for eksempel
Virkedag.TIRSDAG.compareTo(Virkedag.TORSDAG)
returnere en negativ verdi.
Copyright © Kjetil Grønning og Eva Hadler Vihovde, revidert 2014