Helsingin yliopisto / Tietojenkäsittelytieteen laitos / 581258-1 Johdatus ohjelmointiin
Copyright © 1997 Arto Wikla. Tämän oppimateriaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.

4.7 Näkyvyyden säätely

(Muutettu viimeksi 22.11.1998)

Nimien käytettävyydestä eli ns. näkyvyydestä (scope) erilaisissa ohjelmanosissa on ollut puhetta jo monessa yhteydessä. Tässä luvussa kerrataan ja kerätään yhteen Javan tapa hallita nimien näkyvyyttä. (Tässä luvussa ei puututa sisäluokkien näkyvyyskysymyksiin.)

Näkyvyysalueet

Erilaisia näkyvyysalueita - "nimiavaruuksia" - muodostuu seuraaviin rakenteisiin: Huom: Käännösyksikkö ei ole näkyvyysalue! Käännösyksikön luokat tosin kuuluvat aina samaan pakkaukseen.

Javan näkyvyydensäätelymääreillä (access control modifiers) säädellään luokkien, kenttien, konstruktoreiden ja metodeiden näkyvyyttä:

Huom: Nimi ymmärretään Javassa aika väljästi: esimerkiksi x, x(), ja x(3) ovat eri nimiä! Ensimmäinen voi olla vaikka kentän tai paikallisen muuttujan nimi, toinen parametrittoman metodin nimi, kolmas x(int)-metodin nimi, ... Tuotujen (import) pakkausten nimiin voidaan viitata täydellisillä polkunimillä ...

Huom: Java-kääntäjä käyttää hyväksi nimen käyttöyhteyttä nimen merkityksen ymmärtämiseen.

Kielessä on mielestäni sallittu liian laaja nimien kuormittaminen: Perverssi esimerkki teoksesta Arnold, Gosling: The Java Programming Language, Addison-Wesley, 1996:

   class Reuse {
     Reuse Reuse(Reuse Reuse) {
       Reuse:
         for (;;) {
           if (Reuse.Reuse(Reuse) == Reuse)
             break Reuse;
         }
         return Reuse;
     }
   }

Kääntäjälle tuo kelpaa, mutta minä en suostu edes yrittämään ymmärtää! Tuohon tyyliin nimiä käyttämällä voi varmistaa itselleen vaikeuksia niin kokeessa kuin elämässäkin!

Nimet metodissa ja konstruktorissa

Metodin ja konstruktorin paikalliset muuttujat ja muodolliset parametrit ovat nimenomaan paikallisia: Niiden yhteydessä ei voi käyttää näkyvyydensäätelymääreitä. Muuttujat ovat olemassa vain niin kauan kuin metodin tai konstruktorin suoritus on kesken.

[Seuraavassa puhutaan vain metodeista vaikka tarkoitetaan sekä metodeita, että konstruktoreita!]

Metodin paikallisten nimien - muuttujien ja muodollisten parametrien - on oltava yksikäsitteisiä. Mutta paikallisten muuttujien käyttöaluetta metodissa voi rajoittaa alilohkoihin, for-lauseeseen ja lohkojen loppuosiin:

    void m(int p) {
      int i=4;    // p ja i ovat käytettävissä koko metodissa
      i += p;
      { int j=67; // j on käytettävissä vain alilohkossa
        j -= i;
      }
      i += p;
      for (int a=1; a<7; ++a) {
        ...       // a on käytettävissä vain for-lausessa
      }
      int b=77;
      ...         // b on käytettävissä vain metodin loppuosassa
   }
Siis myös alilohkossa määritellyn muutujan nimen on erottava metodin kaikista muista paikallisista nimistä! Toisin sanoen metodin paikallisia nimiä ei voi metodin alilohkoissa peittää. (Katso kuitenkin Lohkot ja muuttujien määrittelylauseet kappaleessa 3.4.)

Paikallisen nimistön lisäksi metodissa voivat olla käytettävissä kaikki metodin oman luokan nimet. Metodi voi paikallisilla nimillään peittää näkyvistä luokan nimiä, mutta tällöinkin luokan nimiin pääsee käsiksi:

Esimerkkejä:
   class Koe1 {
     int x, y;
                 // x ja x() ovat eri nimiä!
     void x() {}

     static int z;

     void m(){
       int x, z=5;
       x = this.x;
       Koe1.z = z;
       x();
     }
   }

   class Koe2 {
     static  int x, y;

     static  void x() {}

     static  void m(){
       int x,z;
       x = Koe2.x;
       x();
     }
   }
 

Nimen merkityksen etsintäjärjestys

Nimen merkitystä etsitään seuraavassa järjestyksessä:
  1. metodin paikalliset nimet, muodolliset parametrit mukaanlukien
  2. oman luokan nimet, luokan perimät nimet mukaanlukien
  3. eksplisiittisesti tuodut luokat, so. import pakkaus.Luokka;
  4. omassa pakkauksessa näkyvät luokat, so. muut kuin private (oman luokan privaattikalusto toki näkyy)
  5. implisiittisesti tuodut luokat, so. import pakkaus.*;
  6. automaattisesti tuodut luokat, so. java.lang ja mahdolliset muut toteutuksen tarjoamat valmiit välineet
Huom: Metodista ei pääse käsiksi minkään toisen metodin paikallisiin muuttujiin.

Nimet ja luokka

Ylläoleva luettelo selvittää myös, miten nimille etsitään merkitys luokan kenttien alustuslausekkeissa ja staattisissa alustuslohkoissa. Luvussa 4.4 tutustustuttiin perityn metodin korvaamiseen ja perityn kentän peittämiseen.

Ilmentymämuuttujien alustuslausekkeissa voidaan viitata sekä edeltäviin ilmentymämuuttujiin että kaikkiin luokkamuuttujiin. Kaikkia arvon palauttavia ilmentymämetodeita ja luokkametodeita voidaan kutsua ilmentymämuuttujien alustuslausekkeissa.

Lapsilta kielletty esimerkki (tätä ei tarvitse ymmärtää):

class Alustus {

  int x = arvo();
  int y = 7;

  int arvo() {return y;}
}

class Akoe {

  public static void main(String[] args) {
    Alustus a = new Alustus();
    System.out.println(a.x+" "+a.y);
  }
} 
Ohjelma tulostaa:
0 7

Luokkamuuttujien (static) alustuslausekkeissa ja staattisissa alustuslohkoissa voidaan viitata edeltäviin luokkamuuttujiin sekä kaikkiin luokkametodeihin, alustuslausekkeissa tietenkin vain arvon palauttaviin luokkametodeihin.

Toinen lapsilta kielletty esimerkki (tätäkään ei tarvitse ymmärtää):

class StaAlustus {

  static int x = arvo();
  static int y = 7;

  static int arvo() {return y;}

  public static void main(String[] args) {
    System.out.println(x+" "+y);
  }
}
Tämäkin ohjelma tulostaa:
0 7



Luokan ja sen piirteiden näkyvyyttä säädellään näkyvyydensäätelymääreillä:

   public class Luokka {   // tarjolla kaikelle maailmalle

     private   int a;      // luokan omaan käyttöön
               int b;      // oman pakkauksen käyttöön
     protected int c;      // pakkauksen ja aliluokkien käyttöön
     public    int d;      // koko maailman käyttöön

     public Luokka() {...} // tällä kaikki saavat konstruoida
                           // ilmentymän luokasta

     private Luokka(int x) { // tällä vain luokka itse saa
       ...                   // konstruoida ilmentymän itsestään 
     }

     // Metodien näkyvyyttä säädellään samalla tavalla.

   }

Luokasta pääsee käsiksi yliluokan korvattuun tai peitettyyn metodiin tai kenttään ilmauksella super, yliluokan konstruktorin voi oman konstruktorin ensimmäisenä lauseena käynnistää ilmauksella super(...)

Nimet ja pakkaus

Samaan pakkaukseen kuuluvat luokat näkevät toistensa ne piirteet, jotka on joko määritelty protected-määrellä tai joilla on oletusnäkyvyys (so. ei määrettä). Nimien etsintäjärjestys (kts. yllä) määrää nimen merkityksen valinnan tässäkin tapauksessa.


Takaisin luvun 4 sisällysluetteloon.