



<html>
<head>
  <title>javabog.dk -  - Nedarvning</title>
  <link rev="stylesheet" type="text/css" href="../typografi.css">
  <meta name="description" content="Lærebog i Java. Af Jacob Nordfalk. Udkommet hos Forlaget Globe">
  <meta name="keywords" content="designmønster, programmering, OOP, objekter, klasser, objektorienteret programmering, Java, JSP, lærebog, UML, IT">
</head>
<body bgcolor="#ffffff">



<a href='http://javabog.dk/'>javabog.dk</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel4.jsp'>&lt;&lt; forrige</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='indhold.jsp'>indhold</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel6.jsp'>n&aelig;ste &gt;&gt;</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kode/'>programeksempler</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='../index_OOP.html'>om bogen</a>

<H1 CLASS="western" STYLE="">5 <a name='afsn5'></a>Nedarvning</H1>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Indhold:</P>
<UL>
  <LI VALUE=1><P LANG="da-DK" CLASS="kapiteloversigt-western">At
  udvide (arve fra) klasser</P>
  <LI><P LANG="da-DK" CLASS="kapiteloversigt-western">Konstrukt&oslash;rer
  og arv</P>
  <LI><P LANG="da-DK" CLASS="kapiteloversigt-western">Polymorfi</P>
  <LI><P LANG="da-DK" CLASS="kapiteloversigt-western">Object-klassen</P>
  <LI><P LANG="da-DK" CLASS="kapiteloversigt-western">St&oslash;rre
  eksempel: Et Matador-spil</P>
</UL>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Kapitlet foruds&aelig;ttes
i resten af bogen.</P>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Foruds&aelig;tter
<a href='kapitel4.jsp'>kapitel 4</a>, Definition af klasser.</P>


<P LANG="da-DK" CLASS="western" STYLE="">I
dette kapitel vil vi se p&aring;, hvordan man kan genbruge
programkode, ved at tage en eksisterende klasse og udbygge den
med flere metoder og variabler (nedarvning).</P>
<H2 CLASS="western">5.1 <a name='afsn5.1'></a>At udbygge eksisterende klasser</SPAN></H2>
<P LANG="da-DK" CLASS="western">Hvad g&oslash;r man, hvis man &oslash;nsker
en klasse, der ligner en eksisterende klasse, men alligevel ikke helt
er den samme?</P>
<P LANG="da-DK" CLASS="western"><SPAN STYLE="font-weight: medium">Svaret
er: Man kan definere underklasser, der </SPAN><B>arver</B><SPAN STYLE="font-weight: medium">
(genbruger en del af koden) fra en anden klasse og kun definerer
den ekstra </SPAN>kode, der skal til for at definere underklassen i
forhold til stamklassen (kaldet superklassen).</P>
<P LANG="da-DK" CLASS="western">Arv (eng.: inheritance) er et meget
vigtigt element i objektorienterede sprog. Med nedarvning kan man
have en hel samling af klasser, der ligner hinanden p&aring; visse
punkter, men som er forskellige p&aring; andre punkter.</P>
<H3 CLASS="western">5.1.1 <a name='afsn5.1.1'></a>Eksempel: En falsk terning</H3>
<P LANG="da-DK" CLASS="western">Hvis man vil snyde i terningspil,
findes der et kendt trick: Bruge sine egne terninger, hvor man har
boret 1'er-sidens hul ud, kommet bly i hullet og malet det p&aelig;nt
over, s&aring; det ikke kan ses. S&aring;dan en terning vil have
meget lille sandsynlighed for at f&aring; en 1-er og en ret stor
sandsynlighed for at f&aring; en 6'er.</P>
<P LANG="da-DK" CLASS="western">Herunder har vi lavet en nedarvning
fra Terning (se <a href='kapitel4.jsp#afsn4.4'>afsnit 4.4</a>) til en ny klasse, FalskTerning, ved at
starte erkl&aelig;ringen med:</P>
<PRE CLASS="ikke-javakode-western">public class FalskTerning extends Terning</PRE><P LANG="da-DK" CLASS="western">
Vi har dermed automatisk overtaget (arvet) alle metoder og variabler
fra Terning-klassen. Dvs. at et FalskTerning1-objekt ogs&aring; har
en v&aelig;rdi-variabel og en toString()-metode.<BR><BR><BR>
</P>
<P LANG="da-DK" CLASS="western">Vi &aelig;ndrer nu klassens opf&oslash;rsel,
ved at definere en anden udgave af kast()-metoden: 
</P>
<PRE CLASS="kode-western"><I>/<SPAN ID="Ramme3" DIR="LTR" STYLE="float: right; width: 3.61cm; height: 6.97cm; border: none; padding: 0cm; background: #ffffff">
  </PRE><P LANG="da-DK" ALIGN=CENTER STYLE="margin-top: 0.11cm; margin-bottom: 0.11cm">
  <IMG SRC="bog6_html_m913d716.gif" NAME="Objekt3" ALIGN=LEFT><BR CLEAR=LEFT><FONT SIZE=2 STYLE="font-size: 9pt"><I>FalskTerning1
  overtager<BR>egenskaber (arver) fra <BR>Terning, men definerer <BR>sin
  egen udgave af kast(). </I></FONT>
  </P>
</SPAN>** En Terning-klasse for falske terninger. */</I>
<PRE CLASS="kode-western">
public class FalskTerning1 <SPAN STYLE="font-style: normal"><B>extends Terning</B></SPAN>
{
  <I>/** tilsides&aelig;t kast med en &quot;bedre&quot; udgave */</I>
<B>  public void kast()</B>
  {
<I>    // udskriv s&aring; vi kan se at metoden bliver kaldt</I>
<I>    // System.out.println(&quot;[kast() p&aring; FalskTerning1] &quot;);</I>

    v&aelig;rdi = (int) (6*Math.random() + 1);

<I>    // er det 1 eller 2? S&aring; lav det om til 6!</I>
    if ( v&aelig;rdi &lt;= 2 ) v&aelig;rdi = 6;
  }
}</PRE>
<P LANG="da-DK" CLASS="western">I klassediagrammet til h&oslash;jre
er nedarvningen vist med en hul pil fra FalskTerning1 til Terning.</P>
<P LANG="da-DK" CLASS="western">Dette kaldes ogs&aring; en
<B>er-en</B>-relation; FalskTerning1 <B>er en</B> Terning, da den jo
har alle de egenskaber (variabler og metoder), en terning har.</P>

<P LANG="da-DK" CLASS="western" STYLE="">Kort
sagt:</P>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">En klasse kan
arve variabler og metoder fra en anden klasse</BLOCKQUOTE>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">Klassen, der
nedarves fra, kaldes superklassen</BLOCKQUOTE>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">Klassen, der
arver fra superklassen, kaldes underklassen</BLOCKQUOTE>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">Underklassen kan
tilsides&aelig;tte (omdefinere) metoder arvet fra superklassen
ved at definere dem igen</BLOCKQUOTE>

<P LANG="da-DK" CLASS="western">Andre steder i litteraturen er der
brugt talrige betegnelser for superklasse, underklasse og
tilsides&aelig;ttelse. Her er et udpluk:<IMG SRC="bog6_html_3630606f.gif" NAME="Objekt7" ALIGN=RIGHT HSPACE=4></P>
<P LANG="da-DK" CLASS="western"><B>Superklasse</B> kaldes ogs&aring;:
Basisklasse, for&aelig;ldreklasse, stamklasse.</P>
<P LANG="da-DK" CLASS="western"><B>Underklasse</B> kaldes ogs&aring;:
Afledt klasse, nedarvet klasse, subklasse.</P>
<P LANG="da-DK" CLASS="western"><SPAN STYLE="font-style: normal"><B>Tilsides&aelig;tte</B></SPAN><SPAN STYLE="font-style: normal">
(eng.: override) kaldes ogs&aring;: omdefinere, overskrive.</SPAN></P>
<P LANG="da-DK" CLASS="western">I vores eksempel er superklassen
Terning. Underklassen FalskTerning1 har tilsidesat metoden kast().</P>

<P LANG="da-DK" CLASS="western">I det f&oslash;lgende program kastes
med to terninger, en rigtig og en falsk:</P>
<PRE CLASS="kode-western">public class Snydespil1
{
  public static void main(String[] arg)
  {
    Terning t1 = new Terning();
    FalskTerning1 <B>t2 = new FalskTerning1()</B>;

    System.out.println(&quot;t1: &quot;+t1); <I>// kunne ogs&aring; kalde t1.toString()</I>
    System.out.println(&quot;t2: &quot;+t2);

    for (int i=0; i&lt;5; i++)
    {
      t1.kast();
<B>      t2.kast();</B>
      System.out.println(&quot;t1=&quot; + t1 + &quot;  t2=&quot; + t2);
      if (t1.v&aelig;rdi == <B>t2.v&aelig;rdi</B>) System.out.println(&quot;To ens!&quot;);
    }
  }
}</PRE>
<HR>
<PRE CLASS="kode-western">t1: 1
t2: 3
t1=1  t2=5
t1=1  t2=6
t1=4  t2=3
t1=6  t2=6
To ens!
t1=2  t2=6</PRE><P LANG="da-DK" CLASS="western">
Vi kan alts&aring; bruge FalskTerning1-objekter p&aring; pr&aelig;cis
samme m&aring;de som Terning-objekter. Bem&aelig;rk, hvordan <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">t2</SPAN></SPAN></FONT></FONT>
giver 6 meget oftere end <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">t1</SPAN></SPAN></FONT></FONT>.</P>
<H3 CLASS="western" STYLE="">5.1.2 <a name='afsn5.1.2'></a>At
udbygge med flere metoder og variabler</H3>
<P LANG="da-DK" CLASS="western">Lad os nu se p&aring; et eksempel,
hvor vi definerer nogle variabler og metoder i nedarvingen.</P>
<IMG SRC="bog6_html_m352a916a.gif" NAME="Objekt4" ALIGN=LEFT><BR CLEAR=LEFT>
<PRE CLASS="kode-western" STYLE="; ">public class FalskTerning2 <B>extends Terning</B>
{
<B>  public int snydev&aelig;rdi;</B>

<B>  public void s&aelig;tSnydev&aelig;rdi(int nySnydev&aelig;rdi)</B>
  {
    snydev&aelig;rdi = nySnydev&aelig;rdi;
  }

<B>  public void kast()</B>
  {
<I>    //System.out.println(&quot;[kast() p&aring; FalskTerning2] &quot;);</I>

    v&aelig;rdi = (int) (6*Math.random() + 1);

    <I>// 1 eller 2? S&aring; lav det om til snydev&aelig;rdi!</I>
    if ( v&aelig;rdi &lt;= 2 ) v&aelig;rdi = snydev&aelig;rdi;
  }
}</PRE><P LANG="da-DK" CLASS="western" STYLE="">
FalskTerning2 har f&aring;et en ekstra variabel, <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">snydev&aelig;rdi</SPAN></SPAN></FONT></FONT>,
og en ekstra metode, s&aelig;tSnydev&aelig;rdi()<A CLASS="sdfootnoteanc" NAME="sdfootnote1anc" HREF="#sdfootnote1sym"><SUP>1</SUP></A>,
der s&aelig;tter <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">snydev&aelig;rdi</SPAN></SPAN></FONT></FONT>
til noget andet.</P>
<PRE CLASS="kode-western" STYLE="">public class Snydespil2
{
  public static void main(String[] arg)
  {
    FalskTerning2 t1 = new FalskTerning2();
    t1.s&aelig;tSnydev&aelig;rdi(4);

    for (int i=0; i&lt;3; i++)
    {
      t1.kast();
      System.out.println(&quot;t1=&quot; + t1);
    }
  }
}</PRE>
<HR>
<PRE CLASS="kode-western" STYLE="">t1=4
t1=4
t1=6</PRE>
<H3 CLASS="western">5.1.3 <a name='afsn5.1.3'></a>N&oslash;gleordet super</H3>
<P LANG="da-DK" CLASS="western">Nogen gange &oslash;nsker man i en
nedarvet klasse, at f&aring; adgang til superklassens metoder, selvom
de m&aring;ske er blevet tilsidesat med en ny definition i
nedarvingen. F.eks. kunne det v&aelig;re rart, hvis vi kunne genbruge
den oprindelige kast()-metode i FalskTerning.</P>
<P LANG="da-DK" CLASS="western">Med <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">super</SPAN></SPAN></FONT></FONT>
refererer man til de metoder, der er kendt for superklassen. Dermed
kan vi skrive en smartere udgave af FalskTerning:</P>
<PRE CLASS="kode-western">public class FalskTerning3 <B>extends Terning</B>
{
  public void kast ()
  {
    <B>super.kast()</B>; <I>// kald den oprindelige kast-metode</I>

    <I>// blev det 1 eller 2? S&aring; lav det om til en 6'er!</I>
    if ( v&aelig;rdi &lt;= 2 ) v&aelig;rdi = 6;
  }
}</PRE><P LANG="da-DK" CLASS="western">
super.kast() kalder kast()-metoden i superklassen. Derefter tager vi
h&oslash;jde for, at det er en falsk terning.</P>
<H3 CLASS="western">5.1.4 <a name='afsn5.1.4'></a>Opgaver</H3>
<OL>
  <LI><P LANG="da-DK" CLASS="western">Lav en LudoTerning, der arver
  fra Terning. Tilsides&aelig;t toString() med en, der giver &quot;*&quot;
  p&aring; en 3er og &quot;globus&quot; p&aring; en 4er (vink: kopi&eacute;r
  Terning's toString()-metode over i LudoTerning og ret i den). Afpr&oslash;v
  klassen. 
  </P>
  <LI><P LANG="da-DK" CLASS="western">Byg videre p&aring; opgave 4.10
  og opret klassen Transportmiddel. Et transportmiddel har en farve,
  et navn, et antal tilbagelagte kilometer og en nypris. Defin&eacute;r
  metoderne<BR><FONT FACE="Courier, monospace"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: #e6e6e6">public
  void bev&aelig;g(int antalKilometer) </SPAN></SPAN></FONT></FONT><FONT FACE="Courier, monospace"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: #e6e6e6"><I>//
  opdaterer antal kilometre              </I></SPAN></SPAN></FONT></FONT><FONT FACE="Courier, monospace"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: #e6e6e6"><BR>public
  double pris()                  </SPAN></SPAN></FONT></FONT><FONT FACE="Courier, monospace"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: #e6e6e6"><I>//
  giver den vurderede salgspris          </I></SPAN></SPAN></FONT></FONT><FONT FACE="Courier, monospace"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: #e6e6e6"><BR>public
  String toString()              </SPAN></SPAN></FONT></FONT><FONT FACE="Courier, monospace"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: #e6e6e6"><I>//
  giver en beskrivelse af transportmidlet</I></SPAN></SPAN></FONT></FONT><BR>Opret
  nedarvingerne Cykel, Skib og Bil med hver sin pris()-metode.</P>
  <LI><P LANG="da-DK" CLASS="western">Forestil dig en virksomhed, hvor
  der er forskellige slags personer: Ansatte og kunder. De ansatte er
  delt op i medarbejdere og ledere. Skits&eacute;r et passende
  klassediagram.</P>
  <LI><P LANG="da-DK" CLASS="western">Lav klasserne til et skak-spil:
  Defin&eacute;r superklassen Brik med egenskaben <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">farve</SPAN></SPAN></FONT></FONT>
  (sort eller hvid), position <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">x</SPAN></SPAN></FONT></FONT>
  og position <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">y</SPAN></SPAN></FONT></FONT>
  (hver mellem 1 og 8). Defin&eacute;r ogs&aring; metoden <BR><FONT FACE="Courier, monospace"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: #e6e6e6">public
  boolean kanFlytteTil(int xNy, int yNy)</SPAN></SPAN></FONT></FONT><FONT FACE="Courier, monospace"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: #e6e6e6"><I>
  // om brikken kan flytte dertil   </I></SPAN></SPAN></FONT></FONT><BR>der
  (for Brik) returnerer sand, hvis positionen eksisterer (<FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">xNy</SPAN></SPAN></FONT></FONT>
  og <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">yNy</SPAN></SPAN></FONT></FONT>
  er mellem 1 og 8).<BR>Defin&eacute;r nedarvingerne Bonde og Taarn
  med tilsidesat kanFlytteTil().</P>
  <LI><P LANG="da-DK" CLASS="western">Lav et system til at arbejde med
  forskellige geometriske figurer.<BR>Opret klassen Figur med
  metoderne beregnAreal() og beregnOmkreds().<BR>Lav nedarvingerne
  Punkt, Linje (med variablen l&aelig;ngde), Cirkel (med variabel
  <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">radius</SPAN></SPAN></FONT></FONT>),
  Rektangel (med variablerne <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">h&oslash;jde</SPAN></SPAN></FONT></FONT>
  og <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">bredde</SPAN></SPAN></FONT></FONT>).</P>
</OL>

<H2 CLASS="western" STYLE="">5.2 <a name='afsn5.2'></a>Polymorfe
variabler</SPAN></H2>
<P LANG="da-DK" CLASS="western"><SPAN ID="Ramme18" DIR="LTR" STYLE="float: right; width: 4.3cm; height: 4.51cm; border: none; padding: 0cm; background: #ffffff">
  <P LANG="da-DK" ALIGN=CENTER STYLE="margin-top: 0.11cm; margin-bottom: 0.11cm">
  <IMG SRC="bog6_html_6a7c39cd.gif" NAME="Objekt23" ALIGN=LEFT><BR CLEAR=LEFT><FONT SIZE=2 STYLE="font-size: 9pt"><I>Snydespil2MedPolymorfi<BR>efter
  punkt A</I></FONT></P>
</SPAN>Se p&aring; f&oslash;lgende eksempel:
</P>
<PRE CLASS="kode-western" STYLE="; ">public class Snydespil2medPolymorfi
{
  public static void main(String[] arg)
  {
    FalskTerning2 ft = new FalskTerning2();
    ft.s&aelig;tSnydev&aelig;rdi(4);

    Terning t;
<B>    t = ft;</B>
                                       <I>// punkt A</I>
    for (int i=0; i&lt;3; i++)
    {
      t.kast();
      System.out.println(&quot;t=&quot; + t);
    }
  }
}</PRE>
<HR>
<PRE CLASS="kode-western" STYLE="">t=4
t=6
t=6</PRE><P LANG="da-DK" CLASS="western">
Hov: Terning-variablen <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">t</SPAN></SPAN></FONT></FONT>
refererer nu pludselig til et FalskTerning2-objekt ?!</P>
<PRE CLASS="kode-western">    t = ft;</PRE><P LANG="da-DK" CLASS="western">
Der er alts&aring; ikke overensstemmelse mellem typen p&aring;
venstre side (Terning) og typen p&aring; h&oslash;jre side
(FalskTerning2). Hvad s&aring; med typesikkerheden?</P>
<H3 CLASS="western">5.2.1 <a name='afsn5.2.1'></a>Dispensation fra traditionel typesikkerhed</H3>
<P LANG="da-DK" CLASS="western">Typesikkerhed g&oslash;r, at man ikke
f.eks. kan tildele et Point-objekt til en Terning-variabel uden at f&aring;
en sprogfejl under overs&aelig;ttelsen. 
</P>
<P LANG="da-DK" CLASS="western">Hvis man kunne det, ville
programmerne kunne indeholde mange fejl, der var sv&aelig;re at
finde. Hvis man f.eks. et eller andet sted i et k&aelig;mpeprogram
havde sat en Terning-variabel til at referere til et
Point-objekt, og det var tilladt, hvad skulle der s&aring; ske, n&aring;r
man s&aring; (m&aring;ske langt senere i en anden del af programmet)
fors&oslash;gte at kalde dette objekts kast()-metode? Et Point-objekt
har jo ingen kast()-metode. Det kunne blive meget sv&aelig;rt at
finde ud af, hvor den forkerte tildeling fandt sted. Sagt med andre
ord: Normalt skal vi v&aelig;re lykkelige for, at Java har denne
regel om typesikkerhed.</P>
<P LANG="da-DK" CLASS="western">Der er imidlertid en meget fornuftig
dispensation fra denne regel:</P>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">En variabel kan
referere til objekter af en underklasse af variablens type</BLOCKQUOTE>
<P LANG="da-DK" CLASS="western">t-variablen har ikke &aelig;ndret
type (det kan variabler ikke), men den peger nu p&aring; et objekt af
typen FalskTerning2. Dette objekt har jo alle metoder og data, som et
Terning-objekt har, s&aring; vi kan ikke f&aring; kaldt
ikke-eksisterende metoder, hvis vi bare &quot;lader som om&quot;, den
peger p&aring; et Terning-objekt. At FalskTerning2-objektet ogs&aring;
har en objektvariabel, snydev&aelig;rdi, og en ekstra metode,
kan vi v&aelig;re ligeglade med, de kan bare ikke ses fra variablen.</P>
<P LANG="da-DK" CLASS="western">Dispensationen giver alts&aring;
mening, fordi en nedarving (f.eks. et FalskTerning2-objekt) set
udefra kan lade, som om det ogs&aring; er af superklassens type (et
Terning-objekt). Udefra har det jo <I>mindst</I> de samme
objektvariabler og metoder, da det har arvet dem.</P>
<P LANG="da-DK" CLASS="western">Selvom variablen <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">t</SPAN></SPAN></FONT></FONT>
af typen Terning refererer til et FalskTerning2-objekt, kan man <I>kun</I>
bruge den til at anvende metoder/variabler i objektet, som stammer
fra Terning-klassen:</P>
<PRE CLASS="kode-western">  t.snydev&aelig;rdi = 4;  <I>// sprogfejl: snydev&aelig;rdi er ikke defineret i Terning</I>
  t.s&aelig;tSnydev&aelig;rdi(4);<I>// sprogfejl: s&aelig;tSnydev&aelig;rdi() er ikke defineret i Terning</I></PRE><H3 CLASS="western">
5.2.2 <a name='afsn5.2.2'></a>Polymorfi</H3>
<P LANG="da-DK" CLASS="western">En anden meget v&aelig;sentlig
detalje omkring denne dispensation er, at det er <I>objektets</I>
type, ikke variablens, der bestemmer, hvilken metodekrop der bliver
udf&oslash;rt, n&aring;r vi kalder en metode:</P>
<PRE CLASS="kode-western">  t.kast();  <I>// kalder FalskTerning2's kast, </I>
<I>            // fordi t peger p&aring; et FalskTerning2-objekt.</I></PRE><P LANG="da-DK" CLASS="western">
Herover kalder vi alts&aring; den kast()-metode, der findes i
FalskTerning2-klassen. Den kigger s&aring;ledes ikke p&aring;
variablen <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">t</SPAN></SPAN></FONT></FONT>'s
type (s&aring; ville den jo bare udf&oslash;re kast()-metoden i
Terning).</P>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">Variablens type
bestemmer, hvilke metoder der kan kaldes</BLOCKQUOTE>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">Objektets type
bestemmer, hvilken metodekrop der bliver udf&oslash;rt</BLOCKQUOTE>
<P LANG="da-DK" CLASS="western">Af samme grund kaldes det at definere
en metode, som allerede findes, fordi den er arvet, for
<I>tilsides&aelig;ttelse</I><SPAN STYLE="font-style: normal"> af
metoden</SPAN>. Man <I>tilsides&aelig;tter</I><SPAN STYLE="font-style: normal">
metodens opf&oslash;rsel med en anden opf&oslash;rsel (en anden
metodekrop).</SPAN></P>
<H3 CLASS="western" STYLE="font-style: normal">5.2.3 <a name='afsn5.2.3'></a>Eksempel p&aring;
polymorfi: Brug af Raflebaeger</H3>
<P LANG="da-DK" CLASS="western" STYLE="font-style: normal">Da
FalskTerning2-objekter ogs&aring; er af type Terning, kan de bruges i
et Raflebaeger:</P>
<PRE CLASS="kode-western">public class SnydeMedBaeger
{
  public static void main(String[] arg)
  {
    Raflebaeger b&aelig;ger = new Raflebaeger(0); <I>// opret et b&aelig;ger med nul terninger</I>

    <B>Terning t</B> = new Terning();
    <B>b&aelig;ger.tilf&oslash;j(t)</B>;    <I>// f&oslash;j en almindelig terning til b&aelig;geret</I>

    <B>FalskTerning2 ft</B> = new FalskTerning2();
    ft.s&aelig;tSnydev&aelig;rdi(6);
    <B>b&aelig;ger.tilf&oslash;j(ft)</B>;   <I>// tilf&oslash;j() f&aring;r et objekt af typen Terning,</I>
<I>                        // og dermed ogs&aring; af typen FalskTerning2.</I>

    ft = new FalskTerning2();
    ft.snydev&aelig;rdi=6;
    <B>t=ft</B>;               <I>// t bruges som mellemvariabel for sjov.</I>
    <B>b&aelig;ger.tilf&oslash;j(t)</B>;

    for (int i=1; i&lt;10; i++)
    {
      b&aelig;ger.ryst();
    }
  }
}</PRE><P LANG="da-DK" CLASS="western">
<SPAN STYLE="font-style: normal">I SnydeMedBaeger kaldes
Raflebaeger's ryst()-metode. Hvis du nu kigger i definitionen af
dennes ryst()-metode (se <a href='kapitel4.jsp#afsn4.5.1'>afsnit 4.5.1</a>), kan du se, at den kalder
kast()-metoden p&aring; de enkelte objekter i
&quot;terninger&quot;-listen</SPAN>:</P>
<PRE CLASS="kode-western">  public void ryst()
  {
    for (Terning t : terninger) 
    {
      t.kast();
    }
  }</PRE><P LANG="da-DK" CLASS="western" STYLE="font-style: normal">
Da to af objekterne, vi har lagt ind i b&aelig;geret, er af typen
FalskTerning2, vil Raflebaeger's ryst()-metode, n&aring;r den kommer
til et objekt af denne type, kalde FalskTerning2's kast() helt
automatisk. Resultatet er alts&aring;, at vi f&aring;r st&oslash;rre
sandsynlighed for at f&aring; seksere.</P>
<P LANG="da-DK" CLASS="western" STYLE="font-style: normal; ">
Faktisk har vi &aelig;ndret den m&aring;de, et Raflebaeger-objekt
opf&oslash;rer sig p&aring;, helt uden at &aelig;ndre i
Raflebaeger-klassen! Raflebaeger ved ikke noget om FalskTerning2, men
kan alligevel bruge den.</P>
<P LANG="da-DK" CLASS="western" STYLE="font-style: normal">En
programm&oslash;r kan alts&aring; lave en Raflebaeger-klasse, som kan
alt muligt smart: Kaste terninger, se hvor mange ens der er,
t&aelig;lle summen af &oslash;jnene, se om der er en stigende f&oslash;lge
(eng.: straight) osv. 
</P>
<P LANG="da-DK" CLASS="western" STYLE="font-style: normal">N&aring;r
en anden programm&oslash;r vil lave en ny slags terning (f.eks. en
snydeterning), beh&oslash;ver han ikke s&aelig;tte sig ind i,
hvordan Raflebaeger-klassen virker og lave tilpasninger af den, for
den kan automatisk bruge hans egen nye slags terning!</P>
<H3 CLASS="western">5.2.4 <a name='afsn5.2.4'></a>Hvilken vej er en variabel polymorf ?</H3>
<P LANG="da-DK" CLASS="western">N&aring;r f&oslash;lgende er muligt:</P>
<PRE CLASS="kode-western">    Terning t;
    FalskTerning2 ft;

    ft = new FalskTerning2();
    t = ft;</PRE><P LANG="da-DK" CLASS="western">
Hvad s&aring; med det omvendte? Kan man tildele en
FalskTerning2-variabel en reference til et objekt af typen Terning? 
</P>
<P LANG="da-DK" CLASS="western">Svaret er: Nej!</P>
<P LANG="da-DK" CLASS="western">D<SPAN ID="Ramme27" DIR="LTR" STYLE="float: right; width: 5.33cm; height: 2.97cm; border: none; padding: 0cm; background: #ffffff">
  <P LANG="da-DK" ALIGN=CENTER STYLE="margin-top: 0.11cm; margin-bottom: 0.11cm">
  <IMG SRC="bog6_html_m72c126ad.gif" NAME="Objekt9" ALIGN=LEFT><BR CLEAR=LEFT><FONT SIZE=2 STYLE="font-size: 9pt"><I>Efter
  punkt A<BR>(programmet vil ikke overs&aelig;tte)</I></FONT></P>
</SPAN>et er jo typen af <FONT FACE="Courier, monospace"><FONT SIZE=2>ft</FONT></FONT>
(FalskTerning2), der bestemmer, hvilke metoder og variabler vi kan
bruge med <FONT FACE="Courier, monospace"><FONT SIZE=2>ft</FONT></FONT>.
Dvs. vi ville kunne skrive:
</P>
<PRE CLASS="kode-western">    Terning t;
    FalskTerning2 ft;

    t = new Terning();
    ft = t;                  <I>// sprogfejl</I>
    ft.snydev&aelig;rdi = 2;</PRE><P LANG="da-DK" CLASS="western">
Hvis den sidste s&aelig;tning kunne udf&oslash;res, ville det v&aelig;re
uheldigt: Terning-objektet som ft refererer til, har jo ingen
<FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">snydev&aelig;rdi</SPAN></SPAN></FONT></FONT>!</P>
<P LANG="da-DK" CLASS="western">Det er alts&aring; et brud p&aring;
typesikkerhedsreglen og Java tillader det derfor ikke. Man m&aring;
ikke kunne kalde noget, der ikke findes p&aring; objektet.</P>
<P LANG="da-DK" CLASS="western">Bem&aelig;rk, at her, som i andre
sammenh&aelig;nge, kigger Java kun p&aring; en linje ad gangen.
F.eks. giver nedenst&aring;ende stadig en sprogfejl, selvom det i
princippet kunne lade sig g&oslash;re:</P>
<PRE CLASS="kode-western">    Terning t;
    FalskTerning2 ft;

    t = new FalskTerning2();
    ft = t;                  <I>// sprogfejl</I>
    ft.snydev&aelig;rdi = 2;</PRE><P LANG="da-DK" CLASS="western">
Her refererer <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">ft</SPAN></SPAN></FONT></FONT>
i sidste linje til et rigtigt FalskTerning2-objekt, og den sidste
linje ville derfor give mening, men programmet kan ikke
overs&aelig;ttes, fordi typesikkerhedsreglen med dispensation ikke er
opfyldt.</P>
<H4 CLASS="western">Et andet eksempel</H4>
<P LANG="da-DK" CLASS="western">Forestiller vi os den generelle
klasse Dyr, med nedarvinger Hest og Hund, kan man skrive</P>
<PRE CLASS="kode-western">    Dyr d = new Hest();</PRE><P LANG="da-DK" CLASS="western">
da en Hest er-et Dyr. Men vi kan ikke skrive</P>
<PRE CLASS="kode-western">    Hest h = new Dyr();</PRE><P LANG="da-DK" CLASS="western">
da Dyr er en generel klasse, der kunne v&aelig;re et hvilket som
helst slags dyr (herunder f.eks. ogs&aring; en hund). Den m&aring;
kan vi ikke l&aelig;gge den ind i en Hest-variabel.</P>
<H3 CLASS="western" STYLE="">5.2.5 <a name='afsn5.2.5'></a>Reference-typekonvertering</H3>
<P LANG="da-DK" CLASS="western">Dispensationen i typesikkerhedsreglen
svarer til den implicitte v&aelig;rditypekonvertering: Ved
konvertering fra int til double beh&oslash;ver programm&oslash;ren
ikke angive eksplicit, at denne v&aelig;rdi skal <I>fors&oslash;ges</I><SPAN STYLE="font-style: normal">
konverteret. N&aring;r en typekonvertering med garanti giver det
&oslash;nskede, laver Java den implicit.</SPAN></P>
<P LANG="da-DK" CLASS="western" STYLE="font-style: normal">I
foreg&aring;ende eksempel s&aring; vi noget, der burde g&aring; godt,
men hvor Javas typeregel forhindrer overs&aelig;ttelse. Her er
vi n&oslash;dt til at bruge eksplicit reference-typekonvertering:</P>
<DIV ID="Ramme14" DIR="LTR" STYLE="float: right; width: 4.3cm; height: 4.51cm; border: none; padding: 0cm; background: #ffffff">
  <P LANG="da-DK" ALIGN=CENTER STYLE="margin-top: 0.11cm; margin-bottom: 0.11cm">
  <IMG SRC="bog6_html_m47f5250.gif" NAME="Objekt5" ALIGN=LEFT><BR CLEAR=LEFT><FONT SIZE=2 STYLE="font-size: 9pt"><I>Efter
  punkt A</I></FONT></P>
</DIV>
<PRE CLASS="kode-western">  Terning t;
  FalskTerning2 ft;

  t = new FalskTerning2();
  ft = (FalskTerning2) t; <I>// OK, men muligvis</I>
                          <I>// k&oslash;retidsfejl</I>

                          <I>// punkt A</I>
  ft.snydev&aelig;rdi = 2;</PRE><P LANG="da-DK" CLASS="western">
Det ligner en almindelig eksplicit v&aelig;rditypekonvertering
(eng.:&nbsp;cast) og Javas betegnelse for det er ogs&aring; det
samme.</P>


<P LANG="da-DK" CLASS="western">Hvis reference-typekonverteringen g&aring;r
galt (det opdages f&oslash;rst under programudf&oslash;relsen),
kommer der en k&oslash;retidsfejl (undtagelsen ClassCastException
opst&aring;r) og programmet stopper.</P>
<P LANG="da-DK" CLASS="western">Der er dog nogle tilf&aelig;lde, hvor
Java, selv n&aring;r man har lavet en reference-typekonvertering,
kan opdage en uheldig konvertering. Hvis de to klasser, der fors&oslash;ges
at konverteres imellem, ikke arver fra hinanden, f&aring;r man en
sprogfejl p&aring; overs&aelig;ttertidspunktet:</P>
<PRE CLASS="kode-western">  Terning t = new Terning();
  Point p;
  p = (Point) t;         <I><SPAN STYLE="font-weight: medium">// Sprogfejl: Point og Terning er urelaterede</SPAN></I></PRE>
<H2 CLASS="western" STYLE="">5.3 <a name='afsn5.3'></a>Eksempel:
Et matador-spil</SPAN></H2>
<P LANG="da-DK" CLASS="western">Med arv kan man skabe et hierarki af
klasser, der ligner hinanden (fordi de har alle f&aelig;llestr&aelig;kkene
fra superklassen) og samtidig kan opf&oslash;re sig forskelligt
(polymorfi).</P>
<P LANG="da-DK" CLASS="western">Her er vist klassediagrammet fra et
matadorspil. Det er en skitse; et rigtigt matadorspil ville indeholde
flere detaljer. 
</P>
<P LANG="da-DK" CLASS="western" ALIGN=CENTER><IMG SRC="bog6_html_5f258a79.gif" NAME="Objekt24"></P>
<P LANG="da-DK" CLASS="western">Klassen Felt, &oslash;verst i
diagrammet, indeholder f&aelig;llestr&aelig;kkene for alle
matadorspillets felter. Alle felter har et navn (f.eks.
&quot;Hvidovrevej&quot;), og derfor definerer vi variablen <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">navn</SPAN></SPAN></FONT></FONT>
her.</P>
<PRE CLASS="kode-western"><I>/** Superklassen for alle matadorspillets felter */</I>
public class Felt
{
  String navn;       <I>// feltets navn, f.eks. &quot;Hvidovrevej&quot;</I>

<I>  /** kaldes n&aring;r en spiller passerer dette felt */</I>
  public void passeret(Spiller sp) 
  {
    sp.besked(&quot;Du passerer &quot;+navn);
  }

<I>  /** kaldes n&aring;r en spiller lander p&aring; dette felt */</I>
  public void landet(Spiller sp)
  {
  }
}</PRE><P LANG="da-DK" CLASS="western">
Alle felter skal ogs&aring; kunne h&aring;ndtere, at spilleren lander
p&aring; eller passerer feltet. Vi forestiller os, at metoderne
landet() og passeret() bliver kaldt af en anden del af programmet,
n&aring;r en spillers brik henholdsvis lander p&aring; eller passerer
feltet. I Felt-klassen er metoderne defineret til ikke at g&oslash;re
noget. 
</P>
<P LANG="da-DK" CLASS="western">Bem&aelig;rk hvordan vi f&aring;r
overf&oslash;rt en reference til spilleren n&aring;r passeret() og
landet() kaldes. Vi skal bruge Spiller-objektet til at kalde metoder
p&aring; spilleren (f.eks. stille sp&oslash;gsm&aring;l eller
overf&oslash;re penge). Her er klassen Spiller med oplysningerne om
spilleren:</P>
<PRE CLASS="kode-western"><I>/** Definition af en spiller */</I>
public class Spiller
{
  String navn;       <I>// spillerens navn, f.eks. &quot;S&oslash;ren&quot;</I>
  double konto;      <I>// antal kroner p&aring; spillerens konto</I>
  int feltnr;        <I>// hvad nummer felt spilleren st&aring;r p&aring;. âStartâ er nummer 0</I>

  public Spiller(String navn, double konto)
  {
    this.navn = navn;
    this.konto = konto;
    feltnr = 0;
  }

<I>  /** En besked til spilleren */</I>
  public void besked(String besked)
  {
    System.out.println(navn+&quot;: &quot;+besked);
  }

<I>  /** Et ja/nej-sp&oslash;rgsm&aring;l. Svarer brugeren ja returneres true, ellers false */</I>
  public boolean sp&oslash;rgsm&aring;l(String sp&oslash;rgsm&aring;l)
  {
    String spm = navn+&quot;: Vil du &quot;+sp&oslash;rgsm&aring;l+&quot;?&quot;;
    String svar = javax.swing.JOptionPane.showInputDialog(spm, &quot;ja&quot;);
    System.out.println(spm+&quot; &quot;+svar);
    if (svar!=null &amp;&amp; svar.equals(&quot;ja&quot;)) return true;
    else return false;
  }

  public void transaktion(double kr)
  {
    konto = konto + kr;
    System.out.println(navn+&quot;s konto lyder nu p&aring; &quot;+konto+&quot; kr.&quot;);
  }

  public void betal(Spiller modtager, double kr)
  {
    System.out.println(navn+&quot; betaler &quot;+modtager.navn+&quot;: &quot;+kr+&quot; kr.&quot;);
    modtager.transaktion(kr);
    transaktion(-kr);
  }
}</PRE>
<P LANG="da-DK" CLASS="western">Under Felt har vi klasserne Helle,
Start, Rederi og Gade, der indeholder data og programkode, som
er specifik for de forskellige slags felter i matadorspillet. De
arver alle fra Felt og er derfor tegnet med en <SPAN STYLE="text-decoration: none"><B>er-en</B></SPAN>-relation
til Felt i diagrammet.</P>
<P LANG="da-DK" CLASS="western">Klassen Helle er simpel; den skal
l&aelig;gge 15000 kr. til spillerens kassebeholdning, hvis spilleren
lander p&aring; feltet. Det g&oslash;res ved at tilsides&aelig;tte
den nedarvede landet()-metode med en, der overf&oslash;rer penge til
spilleren.</P>
<PRE CLASS="kode-western"><I>/** Helle - hvis man lander her f&aring;r man en gevinst */</I>
public class Helle extends Felt
{
  double gevinst;

  public Helle (int gevinst)
  {
    navn = &quot;Helle&quot;;                 <I>// navn er arvet fra Felt</I>
    this.gevinst = gevinst;
  }

  public void landet(Spiller sp)    <I>// tilsides&aelig;t metode i Felt</I>
  {
    sp.besked(&quot;Du lander p&aring; helle og f&aring;r overf&oslash;rt &quot;+gevinst);
    sp.transaktion(gevinst);        <I>// opdater spillers konto</I>
  }
}</PRE><P LANG="da-DK" CLASS="western">
I konstrukt&oslash;ren s&aelig;tter vi feltets navn. Gevinsten ved at
lande her er en parameter til konstrukt&oslash;ren. Metodekaldet
sp.transaktion(gevinst) beder spiller-objektet om at f&oslash;je
gevinsten til kontoen.</P>
<P LANG="da-DK" CLASS="western">Klassen Start skal overf&oslash;re
5000 kr. til spilleren, der passerer eller lander p&aring; feltet.
Dette g&oslash;res ved at tilsides&aelig;tte b&aring;de landet() og
passeret().</P>
<PRE CLASS="kode-western"><I>/** Startfeltet - n&aring;r en spiller passerer dette felt, f&aring;r han 5000 kr */</I>
public class Start extends Felt
{
  double gevinst;

  public Start(double gevinst)
  {
    navn = &quot;Start&quot;;
    this.gevinst = gevinst;
  }

  public void passeret(Spiller sp)                 <I>// tilsides&aelig;t metode i Felt</I>
  {
    sp.besked(&quot;Du passerer start og modtager &quot;+gevinst);
    sp.transaktion(gevinst);                      <I>// kredit/debit af konto</I>
  }

  public void landet(Spiller sp)                   <I>// tilsides&aelig;t metode i Felt</I>
  {
    sp.besked(&quot;Du lander p&aring; start og modtager &quot;+gevinst);
    sp.transaktion(gevinst);
  }
}</PRE><P LANG="da-DK" CLASS="western">
Nu kommer vi til felter, der kan ejes af en spiller, nemlig rederier
og gader. De har en ejer-variabel, der refererer til en Spiller (og
er derfor tegnet med en <SPAN STYLE="text-decoration: none"><B>har</B></SPAN>-relation
til klassen Spiller i diagrammet), en pris og en leje for at lande p&aring;
grunden.</P>
<PRE CLASS="kode-western"><I>/** Et rederi, der kan k&oslash;bes af en spiller */</I>
public class Rederi extends Felt
{
  Spiller ejer;
  double pris;
  double grundleje;

  public Rederi(String navn, double pris, double leje)
  {
    this.navn = navn;
    this.pris = pris;
    this.grundleje = leje;
  }

  public void landet(Spiller sp)
  {
    sp.besked(&quot;Du er landet p&aring; &quot;+navn);
    if (sp==ejer)
    {                                       <I>// spiller ejer selv grunden</I>
      sp.besked(&quot;Det er din egen grund&quot;);
    }
    else if (ejer==null)
    {                                       <I>// ingen ejer grunden, s&aring; k&oslash;b den</I>
      if (sp.konto &gt; pris)
      {
        if (<SPAN STYLE="font-weight: medium">sp.sp&oslash;rgsm&aring;l(&quot;k&oslash;be &quot;+navn+&quot; for &quot;+pris))</SPAN>
        {
          sp.transaktion( -pris );
          ejer=sp;
        }
      }
      else sp.besked(&quot;Du har ikke penge nok til at k&oslash;be &quot;+navn);
    }
    else
    {                                       <I>// feltet ejes af anden spiller</I>
      sp.besked(&quot;Leje: &quot;+grundleje);
      sp.betal(ejer, grundleje);            <I>// spiller betaler til ejeren</I>
    }
  }
}</PRE><P LANG="da-DK" CLASS="western">
N&aring;r en spiller lander p&aring; et rederi, skal der overf&oslash;res
penge fra spilleren til ejeren af grunden. Dette g&oslash;res
ved at tilsides&aelig;tte den nedarvede landet()-metode med en, der
overf&oslash;rer bel&oslash;bet mellem parterne. F&oslash;rst
tjekkes, om spilleren er den samme som ejeren (<FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">sp==ejer</SPAN></SPAN></FONT></FONT>).
Hvis dette ikke er tilf&aelig;ldet, tjekkes, om der ingen ejer er
(<FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">ejer==null</SPAN></SPAN></FONT></FONT>)
og hvis der ikke er, kan spilleren k&oslash;be grunden (<FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">ejer</SPAN></SPAN></FONT></FONT>
s&aelig;ttes lig spilleren). Ellers beordres spilleren til at betale
et bel&oslash;b til ejeren: sp.betal(ejer, grundleje).</P>

<P LANG="da-DK" CLASS="western">Klassen Gade repr&aelig;senterer en
byggegrund og objekter af type Gade har derfor, ud over ejer, pris og
grundleje, en variabel, der husker, hvor mange huse der er bygget p&aring;
dem.</P>
<P LANG="da-DK" CLASS="western">N&aring;r en spiller lander p&aring;
grunden, skal der ske nogenlunde det samme som for et Rederi bortset
fra, at hvis det er ejeren, der lander p&aring; grunden, kan han
bygge et hus.</P>
<PRE CLASS="kode-western"><I>/** En gade, der kan k&oslash;bes af en spiller og bebygges */</I>
public class Gade extends Felt
{
  Spiller ejer;
  double pris;
  double grundleje;
  int antalHuse = 0;
  double huspris;

  public Gade(String navn, double pris, double leje, double huspris)
  {
    this.navn = navn;
    this.pris = pris;
    this.grundleje = leje;
    this.huspris = huspris;
  }

  public void landet(Spiller sp)
  {
    sp.besked(&quot;Du er landet p&aring; &quot;+navn);

    if (sp==ejer)
    {                                          <I>// eget felt</I>
      sp.besked(&quot;Det er din egen grund&quot;);
      if (antalHuse&lt;5 &amp;&amp; sp.konto&gt;huspris &amp;&amp;<I>  // bem&aelig;rk: kun hvis betingelserne</I>
<SPAN STYLE="font-weight: medium">        sp.sp&oslash;rgsm&aring;l(&quot;k&oslash;be hus for &quot;+huspris)</SPAN>)<I>// er opfyldt stilles sp&oslash;rgsm&aring;let</I>
      {                                       <I>// byg et hus</I>
        ejer.transaktion( -huspris );
        antalHuse = antalHuse + 1;
      }
    }
    else if (ejer==null)
    {                                          <I>// ingen ejer grunden, k&oslash;b den?</I>
      if (sp.konto &gt; pris)
      {
        if (<SPAN STYLE="font-weight: medium">sp.sp&oslash;rgsm&aring;l(&quot;k&oslash;be &quot;+navn+&quot; for &quot;+pris))</SPAN>
        {
          sp.transaktion( -pris );
          ejer=sp;
        }
      }
      else sp.besked(&quot;Du har ikke penge nok til at k&oslash;be &quot;+navn);
    }
    else
    {                                          <I>// felt ejes af anden spiller</I>
      double leje = grundleje + antalHuse * huspris;
      sp.besked(&quot;Leje: &quot;+leje);
      sp.betal(ejer, leje);                   <I>// spiller betaler til ejeren</I>
    }
  }
}</PRE><P LANG="da-DK" CLASS="western">
 
</P>

<P LANG="da-DK" CLASS="western" STYLE="">Lad
os nu lave en klasse, der har som ansvarsomr&aring;de at repr&aelig;sentere
et helt matadorspil, med lister over alle felter og spillere og som
holder styr p&aring;, hvis tur det er:</P>
<PRE CLASS="kode-western" STYLE="; ">import java.util.*;

public class Matadorspil
{
  ArrayList&lt;Felt&gt; felter = new ArrayList&lt;Felt&gt;(); <I>// indeholder alle felter</I>

  ArrayList&lt;Spiller&gt; spillere = new ArrayList&lt;Spiller&gt;();  <I>// alle spillere</I>

  int spillersTur = 0;

  public Matadorspil() 
  {
    felter.add(new Start(5000));
    felter.add(new Gade(&quot;R&oslash;dovrevej&quot;, 10000, 400,1000));
    felter.add(new Gade(&quot;Hvidovrevej&quot;,10000, 400,1000));
    felter.add(new Rederi(&quot;Maersk&quot;, 17000,4200));
    felter.add(new Gade(&quot;Gade 3&quot;, 12000, 500,1200));
    felter.add(new Gade(&quot;Gade 4&quot;, 12000, 500,1200));
    felter.add(new Gade(&quot;Gade 5&quot;, 15000, 700,1500));
    felter.add(new Helle(15000));
    felter.add(new Gade(&quot;Frederiksberg All&eacute;&quot;, 20000,1100,2000));
    felter.add(new Gade(&quot;R&aring;dhuspladsen&quot;,      20000,1100,2000));
  }
}</PRE><P LANG="da-DK" CLASS="western">
I konstrukt&oslash;ren bygges en liste med alle felterne p&aring;
br&aelig;ttet op. Alle felterne behandles som objekter af typen Felt
(selvom de egentlig er nedarvinger).</P>

<P LANG="da-DK" CLASS="western">Et spil kan spilles ved at oprette et
Matadorspil-objekt for at f&aring; et br&aelig;t og derp&aring; l&aelig;gge
to spillere ind i spillerlisten. Herunder spilles ogs&aring; 20
runder, hvor hver spiller f&aring;r en tur:</P>
<PRE CLASS="kode-western" STYLE="; ">public class BenytMatadorspil
{
  public static void main(String[] arg)
  {
    Matadorspil spil = new Matadorspil();
    spil.spillere.add(new Spiller(&quot;S&oslash;ren&quot;,50000));   <I>// opret spiller S&oslash;ren</I>
    spil.spillere.add(new Spiller(&quot;Gitte&quot;,50000));   <I>// opret spiller Gitte</I>

<I>    // l&oslash;b gennem 20 runder (40 ture)</I>
    for (spil.spillersTur=0; spil.spillersTur&lt;40; spil.spillersTur++)
    {
      <I>// tag skiftevis S&oslash;ren og Gitte (% er forklaret i <a href='kapitel2.jsp#afsn2.11.4'>afsnit 2.11.4</a>)</I>
      Spiller sp = spil.spillere.get(spil.spillersTur % spil.spillere.size());
      int slag = (int)(Math.random()*6)+1;     <I>// og sl&aring; et terningkast (1-6)</I>
      System.out.println(&quot;***** &quot;+sp.navn+&quot; p&aring; felt &quot;+sp.feltnr+&quot; sl&aring;r &quot;+slag);

      for (int i=1; i&lt;=slag; i=i+1)                  <I>// nu rykkes der</I>
      {
<I>        // g&aring; til n&aelig;ste felt. Hvis vi n&aring;r over antal felter s&aring; t&aelig;l fra 0</I>
        sp.feltnr = (sp.feltnr + 1) % spil.felter.size();
        Felt felt = spil.felter.get(sp.feltnr);

        if (i&lt;slag) felt.passeret(sp); <I>// kald passeret() p&aring; passerede felter</I>
        else felt.landet(sp);          <I>// kald landet() p&aring; sidste felt</I>
        try { Thread.sleep(300); } catch (Exception e) {} <I>// vent 0.3 sek</I>
      }
      try { Thread.sleep(3000); } catch (Exception e) {} <I>// tur slut, vent 3 sek</I>
    }
  }
}</PRE><P LANG="da-DK" CLASS="western">
N&aring;r vi skal spille en tur, finder vi et tilf&aelig;ldigt tal
mellem 1 og 6 og rykker spilleren frem. Vi kalder passeret() p&aring;
hver af de passerede felter og landet() p&aring; den sidste. 
</P>
<P LANG="da-DK" CLASS="western">Bem&aelig;rk hvordan vi s&oslash;rger
for, at variablen <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">feltnr</SPAN></SPAN></FONT></FONT>
vedbliver at have en v&aelig;rdi mellem 0 og antallet af felter med
operatoren %, der giver resten af en division (se <a href='kapitel2.jsp#afsn2.11.4'>afsnit 2.11.4</a>).</P>
<P LANG="da-DK" CLASS="western">Linjen nederst f&aring;r programmet
til at holde en pause i tre sekunder, inden det g&aring;r videre (try
og catch vil blive forklaret i <a href='kapitel14.jsp'>kapitel 14</a>, Undtagelser).</P>
<P LANG="da-DK" CLASS="western" STYLE="">Her
ses uddata fra en k&oslash;rsel af programmet:</P>
<PRE CLASS="kode-western" STYLE="; ">***** S&oslash;ren p&aring; felt 0 sl&aring;r 1
S&oslash;ren: Du er landet p&aring; R&oslash;dovrevej
S&oslash;ren: Vil du k&oslash;be R&oslash;dovrevej for 10000.0? ja
S&oslash;rens konto lyder nu p&aring; 40000.0 kr.
***** Gitte p&aring; felt 0 sl&aring;r 6
Gitte: Du passerer R&oslash;dovrevej
Gitte: Du passerer Hvidovrevej
Gitte: Du passerer Maersk
Gitte: Du passerer Gade 3
Gitte: Du passerer Gade 4
Gitte: Du er landet p&aring; Gade 5
Gitte: Vil du k&oslash;be Gade 5 for 15000.0? ja
Gittes konto lyder nu p&aring; 35000.0 kr.
***** S&oslash;ren p&aring; felt 1 sl&aring;r 1
S&oslash;ren: Du er landet p&aring; Hvidovrevej
S&oslash;ren: Vil du k&oslash;be Hvidovrevej for 10000.0? ja
S&oslash;rens konto lyder nu p&aring; 30000.0 kr.
***** Gitte p&aring; felt 6 sl&aring;r 3
Gitte: Du passerer Helle
Gitte: Du passerer Frederiksberg All&eacute;
Gitte: Du er landet p&aring; R&aring;dhuspladsen
Gitte: Vil du k&oslash;be R&aring;dhuspladsen for 20000.0? ja
Gittes konto lyder nu p&aring; 15000.0 kr.
***** S&oslash;ren p&aring; felt 2 sl&aring;r 5
S&oslash;ren: Du passerer Maersk
S&oslash;ren: Du passerer Gade 3
S&oslash;ren: Du passerer Gade 4
S&oslash;ren: Du passerer Gade 5
S&oslash;ren: Du lander p&aring; helle og f&aring;r overf&oslash;rt 15000.0
S&oslash;rens konto lyder nu p&aring; 45000.0 kr.
***** Gitte p&aring; felt 9 sl&aring;r 1
Gitte: Du lander p&aring; start og modtager 5000.0
Gittes konto lyder nu p&aring; 20000.0 kr.
***** S&oslash;ren p&aring; felt 7 sl&aring;r 1
S&oslash;ren: Du er landet p&aring; Frederiksberg All&eacute;
S&oslash;ren: Vil du k&oslash;be Frederiksberg All&eacute; for 20000.0? ja
S&oslash;rens konto lyder nu p&aring; 25000.0 kr.
***** Gitte p&aring; felt 0 sl&aring;r 3
Gitte: Du passerer R&oslash;dovrevej
Gitte: Du passerer Hvidovrevej
Gitte: Du er landet p&aring; Maersk
Gitte: Vil du k&oslash;be Maersk for 17000.0? ja
Gittes konto lyder nu p&aring; 3000.0 kr.
***** S&oslash;ren p&aring; felt 8 sl&aring;r 5
S&oslash;ren: Du passerer R&aring;dhuspladsen
S&oslash;ren: Du passerer start og modtager 5000.0
S&oslash;rens konto lyder nu p&aring; 30000.0 kr.
S&oslash;ren: Du passerer R&oslash;dovrevej
S&oslash;ren: Du passerer Hvidovrevej
S&oslash;ren: Du er landet p&aring; Maersk
S&oslash;ren: Leje: 4200.0
S&oslash;ren betaler Gitte: 4200.0 kr.
Gittes konto lyder nu p&aring; 7200.0 kr.
S&oslash;rens konto lyder nu p&aring; 25800.0 kr.
***** Gitte p&aring; felt 3 sl&aring;r 3
Gitte: Du passerer Gade 3
Gitte: Du passerer Gade 4
Gitte: Du er landet p&aring; Gade 5
Gitte: Det er din egen grund
Gitte: Vil du k&oslash;be hus for 1500.0? ja
Gittes konto lyder nu p&aring; 5700.0 kr.
***** S&oslash;ren p&aring; felt 3 sl&aring;r 2
S&oslash;ren: Du passerer Gade 3
S&oslash;ren: Du er landet p&aring; Gade 4
S&oslash;ren: Vil du k&oslash;be Gade 4 for 12000.0? ja
S&oslash;rens konto lyder nu p&aring; 13800.0 kr.
***** Gitte p&aring; felt 6 sl&aring;r 1
Gitte: Du lander p&aring; helle og f&aring;r overf&oslash;rt 15000.0
Gittes konto lyder nu p&aring; 20700.0 kr.</PRE><P LANG="da-DK" CLASS="western">
... (og s&aring; videre)</P>

<H3 CLASS="western">5.3.1 <a name='afsn5.3.1'></a>Polymorfi</H3>
<P LANG="da-DK" CLASS="western">Polymorfi vil sige, at objekter af
forskellig type bruges p&aring; en ensartet m&aring;de uden hensyn
til deres pr&aelig;cise type.</P>
<P LANG="da-DK" CLASS="western">Matadorspillet udnytter polymorfi til
at behandle alle feltobjekter ens (ved at kalde landet() og
passeret() fra Spiller's tur()-metode), selvom de er af forskellig
type.</P>
<P LANG="da-DK" CLASS="western">Fidusen er, at programkoden i
tur()-metoden kan skrives uafh&aelig;ngigt af, hvilke felt-typer der
findes: De rigtige landet()- og passeret()-metoder i nedarvingerne
vil automatisk blive kaldt, selvom tur() kun kender til Felt-klassen.</P>
<P LANG="da-DK" CLASS="western">Polymorfi er et kraftfuldt redskab
til at lave meget fleksible programmer, der senere kan udvides, uden
at der skal &aelig;ndres ret meget i den eksisterende kode. 
</P>
<P LANG="da-DK" CLASS="western">For eksempel kan vi til enhver tid
udbygge matadorspillet med flere felttyper uden at skrive programmet
om. Den programkode, der arbejder p&aring; felterne, Spiller-klassens
tur()-metode, kender faktisk slet ikke til andre klasser end Felt!</P>
<P LANG="da-DK" CLASS="western">En foruds&aelig;tning for at udnytte
polymorfi-mekanismen er, at objekterne &quot;s&oslash;rger for sig
selv&quot;, dvs. at data og programkode er i de objekter, som de
handler om.</P>
<H3 CLASS="western">5.3.2 <a name='afsn5.3.2'></a>Opgaver</H3>
<OL>
  <LI><P LANG="da-DK" CLASS="western">Tilf&oslash;j en Bryggeri-klasse
  til matadorspillet og pr&oslash;v, om det virker. P&aring;
  bryggerier afh&aelig;nger lejen af, hvor stort et slag spilleren
  slog, da han landede p&aring; feltet, men lad forel&oslash;big lejen
  v&aelig;re tilf&aelig;ldig.<BR>Du kan evt. kopiere Gade.java i
  stedet for at skrive koden forfra. Husk at inds&aelig;tte et
  Bryggeri i felter-listen i Matadorspil (eller i BenytMatadorspil's
  main()-metode).</P>
  <LI><P LANG="da-DK" CLASS="western">Ret derefter, s&aring; lejen af
  et bryggeri afh&aelig;nger af, hvor stort et slag spilleren
  slog.<BR>Tilf&oslash;j nu objektvariablen <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">slag</SPAN></SPAN></FONT></FONT>
  til Spiller og brug den fra BenytMatadorspil i stedet for den lokale
  variabel (slet den for at undg&aring; misforst&aring;elser). Nu kan
  man altid se, hvad spilleren slog sidst ved at kigge p&aring;
  v&aelig;rdien af <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">slag</SPAN></SPAN></FONT></FONT>.
  Udnyt dette i landet()-metoden i Bryggeri til at lade lejen afh&aelig;nge
  af, hvad spilleren slog.</P>
</OL>
<H2 CLASS="western" STYLE="">5.4 <a name='afsn5.4'></a>Stamklassen
Object</SPAN></H2>
<P LANG="da-DK" CLASS="western">Klassen Object (i pakken java.lang)
er 'alle klassers moder', dvs. superklasse for alle andre klasser.
Arver en klasse ikke fra noget andet, vil den automatisk arve fra
Object.</P>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">Alle klasser
arver fra Object</BLOCKQUOTE>
<P LANG="da-DK" CLASS="western">S&aring;ledes arver f.eks. Terning
fra Object. FalskTerning1 arver indirekte fra Object gennem Terning.
Alle standardklasserne arver ogs&aring; fra Object, muligvis gennem
andre klasser.</P>
<P LANG="da-DK" CLASS="western" ALIGN=CENTER><IMG SRC="bog6_html_m3b79a9cd.gif" NAME="Objekt10"></P>
<P LANG="da-DK" CLASS="western">Det er derfor, at bl.a.
toString()-metoden findes p&aring; alle objekter; den er defineret i
Object og arves til alle klasser i Java. Her ses, hvad der sker, hvis
man udskriver et (f.eks. Boks-) objekt, der ikke har sin egen
toString():</P>
<PRE CLASS="kode-western">...
  Boks b = new Boks();
  System.out.println(&quot;b = &quot;+b);   <I>// b.toString() kaldes implicit</I>
...</PRE>
<HR>
<PRE CLASS="kode-western">  b = Boks@4852d1b0</PRE><P LANG="da-DK" CLASS="western">
Den bruger toString() fra Object og man kan alts&aring; se, at
implementationen af toString() i Object returnerer klassens navn, et
'@' og et tal<A CLASS="sdfootnoteanc" NAME="sdfootnote2anc" HREF="#sdfootnote2sym"><SUP>2</SUP></A>,
f.eks. Boks@4852d1b0.</P>
<P LANG="da-DK" CLASS="western">En anden metode i Object er equals().
Den har vi brugt til at unders&oslash;ge, om strenge er ens, men den
findes alts&aring; p&aring; ethvert objekt og kan f.eks. ogs&aring;
bruges til at unders&oslash;ge, om to lister indeholder de samme
elementer. Bem&aelig;rk, at man selv skal definere equals()-metoden
p&aring; sine egne klasser, f&oslash;r den fungerer som forventet<A CLASS="sdfootnoteanc" NAME="sdfootnote3anc" HREF="#sdfootnote3sym"><SUP>3</SUP></A>.</P>
<H3 CLASS="western">5.4.1 <a name='afsn5.4.1'></a>Referencer til objekter</H3>
<P LANG="da-DK" CLASS="western">N&aring;r alle objekter arver fra
Object, kan man i en variabel af denne type gemme en reference til
ethvert slags objekt, jf. reglerne om typekonvertering:</P>
<PRE CLASS="kode-western">  Object obj;
  obj = new Point();
  obj = &quot;hej&quot;;
  obj = new FalskTerning2();</PRE>
<P LANG="da-DK" CLASS="western">Omvendt kan man ikke rigtig bruge
variablen til noget, f&oslash;r man har lavet en eksplicit
typekonvertering af referencen <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">obj</SPAN></SPAN></FONT></FONT>:</P>
<PRE CLASS="kode-western">  Terning t;
  t = (Terning) obj;
  t.kast();</PRE><P LANG="da-DK" CLASS="western">
Her var vi heldige, at <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">obj</SPAN></SPAN></FONT></FONT>
faktisk refererede til en (underklasse af) Terning. Vi f&aring;r
f&oslash;rst at vide, om typekonverteringen er g&aring;et godt p&aring;
k&oslash;rselstidspunktet.</P>

<P LANG="da-DK" CLASS="western">Tilsvarende kan man bruge Object som
parameter/returtype og f&aring; et fleksibelt, men ikke s&aelig;rlig
sikkert program. Klassen ArrayList benytter sig af dette: Den er en
liste af typen Object, det er derfor, man kan gemme alle slags
objekter i den.</P>
<PRE CLASS="kode-western">  ArrayList l = new ArrayList(); <I>// kunne ogs&aring; skrive new ArrayList&lt;Object&gt;()</I>
  Point p = new Point();
  l.add(p);</PRE><P LANG="da-DK" CLASS="western">
Metoden add() f&aring;r noget af typen Object (dvs. et hvilket som
helst objekt) som parameter.</P>
<P LANG="da-DK" CLASS="western">Nedenfor er vist det samme, men her
bruges en mellemvariabel, der illustrerer, at der sker en implicit
reference-typekonvertering (<FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">p</SPAN></SPAN></FONT></FONT>
skal jo konverteres fra en Point-reference til en Object-reference):</P>
<PRE CLASS="kode-western">  ArrayList l = new ArrayList(); <I>// liste med alle slags objekter (type Object)</I>
  Point p = new Point();
  Object obj;                    <I>// overfl&oslash;dig mellemvariabel</I>
  obj = p;                       <I>// implicit reference-typekonvertering</I>
  l.add(obj);</PRE><P LANG="da-DK" CLASS="western">
N&aring;r man kalder get() for at f&aring; fat i objektet igen, er
det n&oslash;dvendigt med en eksplicit reference-typekonvertering,
fordi konverteringen sker den anden vej, fra superklasse til
underklasse:</P>
<PRE CLASS="kode-western">  p = (Point) l.get(0);</PRE><P LANG="da-DK" CLASS="western">
Igen vises det samme blot med mellemvariabel, s&aring; man kan se,
hvilken typekonvertering der finder sted:</P>
<PRE CLASS="kode-western">  o = l.get(0);        <I>// ingen konvertering</I>
  p= (point) o;        <I>// eksplicit reference-typekonvertering</I></PRE>
<H2 CLASS="western" STYLE="">5.5 <a name='afsn5.5'></a>Konstrukt&oslash;rer
i underklasser</SPAN></H2>
<P LANG="da-DK" CLASS="western">Vi minder om, at:</P>
<UL>
  <LI><P LANG="da-DK" CLASS="western">Konstrukt&oslash;rer definerer,
  hvordan objekter oprettes og med hvilke parametre de m&aring;
  oprettes. Der kan kun oprettes objekter p&aring; en m&aring;de, der
  passer med en konstrukt&oslash;r.</P>
  <LI><P LANG="da-DK" CLASS="western">Hvis en klasse ikke har
  defineret nogen konstrukt&oslash;rer, s&aring; defineres automatisk
  en standardkonstrukt&oslash;r (uden parametre og med tom
  metodekrop). F.eks. har Boks automatisk f&aring;et en tom
  konstrukt&oslash;r, s&aring; den kunne oprettes med new Boks().</P>
</UL>
<P LANG="da-DK" CLASS="western">Underklassen skal selv definere,
hvordan dets objekter skal kunne oprettes, s&aring; den skal selv
definere sine konstrukt&oslash;rer. Den kan have f&aelig;rre eller
flere konstrukt&oslash;rer end superklassen.</P>
<P LANG="da-DK" CLASS="western">N&aring;r man definerer en
konstrukt&oslash;r p&aring; en underklasse, skal man kun initialisere
den nye del af objektet. Har man f.eks. tilf&oslash;jet nye
variabler, skal konstrukt&oslash;ren initialisere dem.</P>
<P LANG="da-DK" CLASS="western">Den arvede del af objektet
initialiseres ved, at man fra underklassens konstrukt&oslash;r kalder
en konstrukt&oslash;r fra superklassen. Dette g&oslash;res med
s&aelig;tningen: &quot;super(...);&quot; med eventuelle parametre.
Man bruger alts&aring; her <B>super</B> som en metode. Det skal g&oslash;res
som den <B>f&oslash;rste</B> s&aelig;tning i konstrukt&oslash;ren.
Hvis man ikke selv kalder super() som det f&oslash;rste, sker der
det, at super bliver kaldt automatisk <B>uden</B><SPAN STYLE="font-weight: medium">
parametre.</SPAN></P>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">Konstrukt&oslash;rer
skal defineres separat i en underklasse</BLOCKQUOTE>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">En konstrukt&oslash;r
kalder f&oslash;rst en af superklassens konstrukt&oslash;rer</BLOCKQUOTE>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">Superklassens
konstrukt&oslash;r kan kaldes med: super(<I>parametre</I>)</BLOCKQUOTE>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">Hvis man ikke
selv kalder en af superklassens konstrukt&oslash;rer, inds&aelig;tter
Java automatisk et kald af superklassens konstrukt&oslash;r uden
parametre</BLOCKQUOTE>

<P LANG="da-DK" CLASS="western">Herunder definerer vi
Boks3medDensitet, der arver fra Boks3 (se <a href='kapitel4.jsp#afsn4.3'>afsnit 4.3</a>). Det nye er
variablen <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">densitet</SPAN></SPAN></FONT></FONT>
og metoden v&aelig;gt(). Objektet kan oprettes med: new
Boks3medDensitet(), som opretter boksen med standardv&aelig;rdier
eller med: new Boks3medDensitet(lgd,b,h,d), hvor man angiver l&aelig;ngde,
bredde, h&oslash;jde og massefylden (densitet).<SPAN ID="Ramme28" DIR="LTR" STYLE="float: left; width: 3.6cm; height: 6.76cm; border: none; padding: 0cm; background: #ffffff">
  <P LANG="da-DK" ALIGN=CENTER STYLE="margin-top: 0.11cm; margin-bottom: 0.11cm">
  <IMG SRC="bog6_html_m4753e43b.gif" NAME="Objekt11" ALIGN=LEFT><BR CLEAR=LEFT><FONT SIZE=2 STYLE="font-size: 9pt"><I>Boks3medDensitet
  tillader<BR>oprettelse p&aring; andre m&aring;der<BR>end
  superklassen Boks3</I></FONT></P>
</SPAN><BR CLEAR=LEFT>
</P>
<PRE CLASS="kode-western" STYLE="; ">public class Boks3medDensitet extends Boks3
{
  private double densitet;

  public Boks3medDensitet()
  {
<I>    // <B>super();</B> kaldes automatisk hvis intet andet angives</I>
    densitet = 10.0;
  }

  public Boks3medDensitet(double lgd, double b,  
          double h, double densitet)
  {
    <I>// v&aelig;lg en anden konstrukt&oslash;r i superklassen </I>
<I>    // end den uden parametre</I>
<B>    super(lgd,b,h);</B> 
    this.densitet = densitet;
  }

  public double v&aelig;gt()
  {
    <I>// superklassen udregner volumen for os</I>
    return volumen() * densitet; 
  }
}</PRE><H3 CLASS="western" STYLE="; ">
5.5.1 <a name='afsn5.5.1'></a>Konsekvenser</H3>
<P LANG="da-DK" CLASS="western">Ovenst&aring;ende regler kombineret
med reglerne for standardkonstrukt&oslash;ren har nogle pudsige
konsekvenser. Lad os se p&aring; et eksempel med al overfl&oslash;dig
kode sk&aring;ret v&aelig;k:</P>
<PRE CLASS="ikke-javakode-western">public class A
{
  public A(int i) { }
}</PRE>
<PRE CLASS="ikke-javakode-western">public class B extends A
{
}</PRE>
<P LANG="da-DK" CLASS="western">Klassen B vil ikke overs&aelig;tte,
fordi den af Java vil blive lavet om til:</P>
<PRE CLASS="ikke-javakode-western">public class B extends A
{
  public B() <I>// standardkonstrukt&oslash;r inds&aelig;ttes automatisk af Java</I>
  {
    super();
  }
}</PRE><P LANG="da-DK" CLASS="western">
Standardkonstrukt&oslash;ren i B vil alts&aring; pr&oslash;ve at
kalde konstrukt&oslash;ren i A uden parametre, men den findes jo
ikke, fordi A har en anden konstrukt&oslash;r. Overs&aelig;tteren
kommer med fejlmeddelelsen &quot;<I>constructor A() not found</I>&quot;.
S&aring; er vi n&oslash;dt til at angive konstrukt&oslash;ren i
superklassen:</P>
<PRE CLASS="ikke-javakode-western">public class B extends A
{
  public B()    <I>// vi er n&oslash;dt til at definere underklassens konstrukt&oslash;r...</I>
  {
    super(42); <I>// ... s&aring; vi kan angive parametre til superklassens konstrukt&oslash;r</I>
  }
}</PRE><P LANG="da-DK" CLASS="western">
Hvis vi &oslash;nsker en konstrukt&oslash;r med de samme parametre i
B som i A, vil det se s&aring;ledes ud:</P>
<PRE CLASS="ikke-javakode-western">public class B extends A
{
  public B(int i)    <I>// vi er n&oslash;dt til at definere underklassens konstrukt&oslash;r...</I>
  {
    super(i); <I>// ... s&aring; vi kan angive parametre til superklassens konstrukt&oslash;r</I>
  }
}</PRE>
<P LANG="da-DK" CLASS="western">Der er til geng&aelig;ld ingen
problemer med:</P>
<PRE CLASS="ikke-javakode-western">public class A2
{
}</PRE><P LANG="da-DK" CLASS="western" STYLE="margin-top: 0cm; margin-bottom: 0cm">
og</P>
<PRE CLASS="ikke-javakode-western">public class B2 extends A2
{
}</PRE>
<P LANG="da-DK" CLASS="western">Java laver det om til:</P>
<PRE CLASS="ikke-javakode-western">public class A2
{
  public A2() { } <I>// inds&aelig;ttes automatisk af Java</I>
}</PRE><P LANG="da-DK" CLASS="western" STYLE="margin-top: 0cm; margin-bottom: 0cm">
og</P>
<PRE CLASS="ikke-javakode-western">public class B2 extends A2
{
  public B2()     <I>// inds&aelig;ttes automatisk af Java</I>
  {
    super();
  }
}</PRE>
<H2 CLASS="western" STYLE="">5.6 <a name='afsn5.6'></a>Ekstra
eksempler</SPAN></H2>
<H3 CLASS="western">5.6.1 <a name='afsn5.6.1'></a>Matadorspillet version 2</H3>
<P LANG="da-DK" CLASS="western">Dette eksempel viser, hvordan man kan
spare programkode (og dermed programmeringstid) med nedarvning.
Samtidig viser det brugen af konstrukt&oslash;rer i underklasser.</P>
<P LANG="da-DK" CLASS="western">Se igen p&aring; programkoden til
Rederi og Gade. Der er meget programkode, som er ens for de to
klasser. Faktisk implementerer de kode, der er f&aelig;lles for alle
grunde, der kan ejes af en spiller og derfor vil det v&aelig;re
hensigtsm&aelig;ssigt, at f&oslash;lgende kode var i en Grund-klasse:</P>
<UL>
  <LI><P LANG="da-DK" CLASS="western">Definition og initialisering af
  variablerne <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">pris</SPAN></SPAN></FONT></FONT>,
  <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">grundleje</SPAN></SPAN></FONT></FONT>,
  <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">ejer</SPAN></SPAN></FONT></FONT>.</P>
  <LI><P LANG="da-DK" CLASS="western">H&aring;ndtering af, at en
  spiller lander p&aring; grunden (bl.a. betaling af leje).</P>
  <LI><P LANG="da-DK" CLASS="western">H&aring;ndtering af, at en
  spiller lander p&aring; en grund, der ikke ejes af nogen (k&oslash;b
  af grunden).</P>
</UL>
<P LANG="da-DK" CLASS="western">Det har vi gjort herunder. 
</P>
<P LANG="da-DK" CLASS="western">Vi har v&aelig;ret forudseende og
flyttet beregningen af lejen ud fra landet() og ind i en separat
metode beregnLeje(), fordi netop denne er meget forskellig for Rederi
og Gade.</P>
<PRE CLASS="kode-western"><I>/** Mellemklasse mellem 'Felt' og underliggende klasser som Gade og Rederi */</I>

public class Grund2 extends Felt
{
<B>  Spiller ejer;</B>
<B>  double pris;</B>
<B>  double grundleje;</B>

<B>  public Grund2(String navn, double pris, double leje)</B>
  {
    this.navn=navn;
    this.pris=pris;
    this.grundleje=leje;
  }

<B>  public double beregnLeje()</B>
  {
    return grundleje;
  }

<B>  public void landet(Spiller sp)</B>
  {
    sp.besked(&quot;Du er landet p&aring; &quot;+navn);
    if (sp==ejer)
    {                                       <I>// spiller ejer feltet</I>
      sp.besked(&quot;Det er din egen grund&quot;);
    }
    else if (ejer==null)
    {                                       <I>// ingen ejer grunden, s&aring; k&oslash;b den</I>
      if (sp.konto &gt; pris)
      {
        if (<SPAN STYLE="font-weight: medium">sp.sp&oslash;rgsm&aring;l(&quot;k&oslash;be &quot;+navn+&quot; for &quot;+pris))</SPAN>
        {
          sp.transaktion( -pris );
          ejer=sp;
        }
      }
      else sp.besked(&quot;Du har ikke penge nok til at k&oslash;be &quot;+navn);
    }
    else
    {                                       <I>// felt ejes af anden spiller</I>
      double leje = <B>beregnLeje()</B>;           <I>// udregn lejen</I>
      sp.besked(&quot;Leje: &quot;+leje);
      sp.betal(ejer, leje);                 <I>// spiller betaler til ejeren</I>
    }
  }
}</PRE>

<P LANG="da-DK" CLASS="western" STYLE="">Nu
er Rederi ret nem. Den skal nemlig (i denne simple udgave) opf&oslash;re
sig pr&aelig;cis som Grund2. Vi skal blot definere konstrukt&oslash;ren,
som kalder den samme konstrukt&oslash;r i Grund2:</P>
<PRE CLASS="kode-western"><I>/** Et rederi, der kan k&oslash;bes af en spiller */</I>

public class Rederi2 extends Grund2 
{
<B>  public Rederi2(String navn, double pris, double leje)</B>
  {
<B>    super(navn, pris, leje);</B> <I>// overf&oslash;r v&aelig;rdierne til superklassens konstrukt&oslash;r</I>
  }
}</PRE>
<P LANG="da-DK" CLASS="western">Nu kommer vi til Gade. Her er
beregnLeje() tilsidesat til ogs&aring; at tage h&oslash;jde for
antallet af huse. Med <B>super</B> kan vi faktisk spare en hel del
arbejde. Gaderne kan genbruge meget af landet()-metoden, men der er
dog en ekstra mulighed for at bygge hus. Derfor kalder vi
superklassens landet()-metode. Derefter, hvis spilleren, der er
landet p&aring; gaden, er ejeren, pr&oslash;ver vi at bygge et hus.</P>
<PRE CLASS="kode-western"><I>/** En gade, der k&oslash;bes af en spiller og bebygges */</I>

public class Gade2 extends Grund2
{
  int antalHuse;                             <I>// antal huse og pris</I>
  double huspris;

<B>  public Gade2(String navn, double pris, double leje, double huspris)</B>
  {
<B>    super(navn, pris, leje);</B>                 <I>// kald Grund2's konstrukt&oslash;r</I>
    this.huspris=huspris;
    antalHuse = 0;
  }

<B>  public double beregnLeje()</B>                 <I>// tilsides&aelig;t Grund2's beregnLeje()</I>
  {
    return grundleje + antalHuse * huspris;
  }

  public void landet(Spiller sp)
  {
    <B>super.landet(sp)</B>;                        <I>// brug gamle landet()</I>
    if (sp==ejer)
    {                                        <I>// eget felt; byg hus?</I>
      if (antalHuse&lt;5 &amp;&amp; sp.konto&gt;huspris &amp;&amp; <SPAN STYLE="font-weight: medium">sp.sp&oslash;rgsm&aring;l(&quot;k&oslash;be hus for &quot;+pris)</SPAN>)
      {                                     <I>// byg et hus</I>
        ejer.transaktion( -huspris );
        antalHuse = antalHuse + 1;
        sp.besked(&quot;Du bygger hus p&aring; &quot;+navn+&quot; for &quot;+huspris);
      }
    }
  }
}</PRE>
<P LANG="da-DK" CLASS="western">Denne m&aring;de at programmere p&aring;
kan spare meget kode: L&aelig;g m&aelig;rke til, at vi har sparet
n&aelig;sten halvdelen af koden v&aelig;k i de to nye klasser!</P>

<P LANG="da-DK" CLASS="western" STYLE="">Herunder
ses klassediagrammet for de nye klasser. 
</P>
<P LANG="da-DK" CLASS="western">Da Grund2 <B>har en</B> spiller
(ejeren), er der en pil fra Grund2 til Spiller, en <B>har</B>-relation.
Resten af pilene symboliserer <B>er-en</B>-relationer, f.eks. Gade2
<B>er en</B> Grund2, Grund2 <B>er et</B> Felt.</P>
<P LANG="da-DK" CLASS="western" ALIGN=CENTER><IMG SRC="bog6_html_m7515a896.gif" NAME="Objekt26"></P>
<P LANG="da-DK" CLASS="western">I <a href='kapitel22.jsp'>kapitel 22</a>, Objektorienteret analyse og design
kan du l&aelig;se mere om, hvordan man designer sine klasser og
hvordan man v&aelig;lger hvilke er-en- og har-en-relationer, der skal
v&aelig;re mellem klasserne.</P>
<H3 CLASS="western">5.6.2 <a name='afsn5.6.2'></a>Opgaver</H3>
<OL>
  <LI><P LANG="da-DK" CLASS="western">Ret i Matadorspil.java til at
  bruge Rederi2 og Gade2 i stedet for Rederi og Gade. K&oslash;r
  programmet og f&oslash;lg med i, hvad der sker i Gade2's
  konstrukt&oslash;r og landet()-metode.</P>
  <LI><P LANG="da-DK" CLASS="western">F&oslash;j en Bryggeri-klasse
  til matadorspillet version 2. Husk at kalde super() i konstrukt&oslash;ren
  (hvorfor er det n&oslash;dvendigt?). Du kan evt. kopiere Gade2.java
  i stedet for at skrive koden forfra. Hvor meget kode kan du
  spare?</P>
</OL>

<H2 CLASS="western" STYLE="">5.7 <a name='afsn5.7'></a>Test dig
selv</SPAN></H2>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel5.jsp#afsn5.7">
  <input type='checkbox' name='vis' value='5.7'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='5.7'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H2 CLASS="western">5.8 <a name='afsn5.8'></a>Resum&eacute;</SPAN></H2>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel5.jsp#afsn5.8">
  <input type='checkbox' name='vis' value='5.8'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='5.8'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H2 CLASS="western" STYLE="">5.9 <a name='afsn5.9'></a>Avanceret</SPAN></H2>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel5.jsp#afsn5.9">
  <input type='checkbox' name='vis' value='5.9'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='5.9'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H3 CLASS="western">5.9.1 <a name='afsn5.9.1'></a>Initialisering uden for konstrukt&oslash;rerne</H3>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel5.jsp#afsn5.9.1">
  <input type='checkbox' name='vis' value='5.9.1'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='5.9.1'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H3 CLASS="western" STYLE="">5.9.2 <a name='afsn5.9.2'></a>Kald af
en konstrukt&oslash;r fra en anden konstrukt&oslash;r</H3>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel5.jsp#afsn5.9.2">
  <input type='checkbox' name='vis' value='5.9.2'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='5.9.2'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H3 CLASS="western">5.9.3 <a name='afsn5.9.3'></a>Metoder erkl&aelig;ret final</H3>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel5.jsp#afsn5.9.3">
  <input type='checkbox' name='vis' value='5.9.3'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='5.9.3'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H3 CLASS="western">
5.9.4 <a name='afsn5.9.4'></a>Metoder erkl&aelig;ret protected</H3>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel5.jsp#afsn5.9.4">
  <input type='checkbox' name='vis' value='5.9.4'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='5.9.4'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H3 CLASS="western">5.9.5 <a name='afsn5.9.5'></a>Variabel-overskygning</H3>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel5.jsp#afsn5.9.5">
  <input type='checkbox' name='vis' value='5.9.5'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='5.9.5'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  
<DIV ID="sdfootnote1">
  <P LANG="da-DK" CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote1sym" HREF="#sdfootnote1anc">1</A>Egentlig
  er s&aelig;tSnydev&aelig;rdi() overfl&oslash;dig, da snydev&aelig;rdi
  er public, men vi skal bruge den til at illustrere en pointe i
  n&aelig;ste afsnit.</P>
</DIV>
<DIV ID="sdfootnote2">
  <P LANG="da-DK" CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote2sym" HREF="#sdfootnote2anc">2</A>En
  s&aring;kaldt hashkode, der med stor sandsynlighed er unik. Dette er
  ikke specielt informativt, s&aring; man definerer ofte sin egen
  toString()</P>
</DIV>
<DIV ID="sdfootnote3">
  <P LANG="da-DK" CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote3sym" HREF="#sdfootnote3anc">3</A>Man
  kan altid kalde equals(), men det kan godt v&aelig;re, at den ikke
  giver det forventede, hvis den ikke er defineret i klassen.
  ArrayList og String har defineret den, mens f.eks. StringBuilder
  ikke har. Da f&aring;r man opf&oslash;rslen fra Object, der kun
  unders&oslash;ger, om objekterne er de samme (samme sted i
  hukommelsen), den tager ikke h&oslash;jde for, om to objekter
  indeholder ens data.</P>
</DIV>

<a href='http://javabog.dk/'>javabog.dk</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel4.jsp'>&lt;&lt; forrige</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='indhold.jsp'>indhold</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel6.jsp'>n&aelig;ste &gt;&gt;</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kode/'>programeksempler</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='../index_OOP.html'>om bogen</a>
<hr>
<font size=-2>http://javabog.dk/ - <b></b> af Jacob Nordfalk.
<br>
  Licens og kopiering under <a href='http://www.linuxbog.dk/licens.html'>&Aring;ben Dokumentlicens</a> (&Aring;DL)
  hvor intet andet er nævnt (79% af værket).
</font>
<br>
Ønsker du at se de sidste 21% af dette værk (226970 tegn)
skal du købe bogen. Så får du pæne figurer og layout, stikordsregister og en trykt bog med i købet.
<!-- netlæser: Wget/1.10, autoHent: true  -->
     

</body>
</html>
