2.2. Modificētāji, abstraktas klases un interfeisi

Ievads

Mērķi

  • Raksturot statiskos (ar modificētāju static) mainīgos un metodes, kā arī statiskās inicializācijas blokus
  • Raksturot konstantās (ar final) klases, metodes un mainīgos
  • Izskaidrot, kā un kad lietot abstract klases un metodes
  • Izskaidrot, kā un kad lietot iekšējās klases
  • Definēt atšķirību starp statiskām un nestatiskām iekšējām klasēm
  • Izskaidrot, kā un kad lietot interfeisu

    Javas programmā prast atrast:

  • static metodes un atribūtus
  • final metodes un atribūtus
  • Iekšējas klases
  • Interfeisus un abstraktas klases
  • Abstraktas metodes

Ievadjautājumi

  • Kā var izveidot konstanti?
  • Kā izveidot instances mainīgo, kuru uzstāda vienreiz un pēc tam nemaina (pat no pašas klases)?
  • Kā deklarēt datus, kuri ir kopīgi visām dotās klases instancēm?
  • Kā var aizliegt veidot klasei apakšklases vai arī metodei - pārdefinētās metodes?
  • Kā var izveidot vairākas klases, kuras implementē kopīgu interfeisu, bet nepieder kopīgam mantošanas kokam?

Atslēgvārds static

  • Atslēgvārdu static lieto kā modifikatoru mainīgajiem, metodēm un iekšējām klasēm.
  • Atslēgvārds static deklarē, ka atribūts vai metode ir saistīti ar klasi kopumā, nevis ar atsevišķu klases instanci.
  • Statiskos locekļus bieži sauc par "klases locekļiem", piemēram, "klases atribūti" vai "klases metodes".

Klašu jeb statiskie atribūti

Ir kopīgi visām klases instancēm:

Diagramma ar statisku klases locekli 'counter'
1  public class Count {
2    private int serialNumber;
3    public static int counter = 0;
4  
5    public Count() {
6      counter++;
7      serialNumber = counter;
8    }
9  }

Klašu jeb statiskie atribūti

public static mainīgajiem var piekļut no ārpuses, lietojot tikai klases vārdu, nenorādot klases instanci:

1  public class OtherClass {
2    public void incrementNumber() {
3      Count.counter++;
4    }
5  }

Klašu jeb statiskās metodes

Varat izsaukt static metodi, nenorādot instanci tai klasei, kurā ir šī metode:

1  public class Count {
2      private int serialNumber;
3      private static int counter = 0;
4  
5      public static int getTotalCount() {
6          return counter;
7      }
8  
9      public Count() {
10          counter++;
11          serialNumber = counter;
12      }
13  }

1  public class TestCounter {
2    public static void main(String[] args) {
3      System.out.println("Number of counter is "
4                         + Count.getTotalCount());
5      Count count1 = new Count();
6      System.out.println("Number of counter is "
7                         + Count.getTotalCount());
8    }
9  }

TestCounter programmas izvads ir:

Number of counter is 0
Number of counter is 1

Statiskās inicializācijas bloki (Static initializers)

  • Klase var ierakstīt kodu statiskajā blokā (static block), kurš neatrodas nevienas metodes ķermenī
  • Statiskais bloks izpildās tikai vienreiz, kad klasi ielādē
  • Statisko bloku parasti lieto, lai inicializētu statiskos jeb klašu atribūtus

Statiskās inicializācijas bloki

1  public class Count {
2    public static int counter;
3    static {
4      counter = Integer.getInteger("myApp.Count.counter").intValue();
5    }
6  }
7  
8  public class TestStaticInit {
9    public static void main(String[] args) {
10      System.out.println("counter = " + Count.counter);
11    }
12  }

TestStaticInit programmas izvade ir:

java -DmyAppCount.counter=47 TestStaticInit
counter = 47

Projektēšanas šablons (Design Pattern) - "Singleton"

Projektēšanas šablonam tipiska objektu diagramma

Projektēšanas šablona "Singleton" implementācija

Singleton'a izejas teksts:

1  package shipping.domain;
2  
3  public class Company {
4      private static Company instance = new Company();
5      private String name;
6      private Vehicle[] fleet;
7  
8      public static Company getCompany() {
9          return instance;
10     }
11  
12     private Company() {...}
13  
14     // more Company code ...
15  }

Var darīt vēl taupīgāk - saukt konstruktoru no getCompany(), ja instance==null, t.i. singleton'a instanci konstruē tikai tad, ja to tiešām kādam vajag. Lietojuma piemērs:

1  package shipping.reports;
2  
3  import shipping.domain.*;
4  
5  public class FuelNeedsReport {
6      public void generateText(PrintStream output) {
7          Company c = Company.getCompany();
8          // use Company object to retrieve the fleet vehicles
9      }
10  }

Atslēgvārds final

  • final klasei nevar veidot apakšklases
  • final metodi nevar pārdefinēt
  • final mainīgais ir konstante
  • final mainīgajam vērtību var piešķirt tikai vienreiz, bet šī piešķiršana var notikt vēlāk par deklarēšanu; tikmēr to sauc par "neinicializētu konstants mainīgo" ("blank final variable.").
  • Instances līmeņa neinicializēts konstants mainīgais ir jāuzstāda katrā konstruktorā
  • Lokāls neinicializēts konstants mainīgais ir jāuzstāda metodes ķermenī pirms to sāk lietot.

Konstanti mainīgie

"Globālas konstantes":

public class Bank {
    private static final double DEFAULT_INTEREST_RATE=3.2;
    ... // citas deklarācijas
}

Neinicializēts konstants instanču atribūts:

public class Customer {
    private final long customerID;

    public Customer() {
        customerID = createID();
    }
    public long getID() {
        return customerID;
    }
    private long createID() {
        return ... // ģenerē jaunu ID
    }
    ... // citas deklarācijas
}

Abstraktas klases: Scenārijs

Situācija ar polimorfu masīvu

Transportlīdzekļu saimes inicializācija:

1   public class ShippingMain {
2       public static void main(String[] args) {
3           Company c = Company.getCompany();
4  
5           // populate the company with a fleet of vehicles
6           c.addVehicle( new Truck(10000.0) );
7           c.addVehicle( new Truck(15000.0) );
8           c.addVehicle( new RiverBarge(500000.0) );
9           c.addVehicle( new Truck(9500.0) );
10          c.addVehicle( new RiverBarge(750000.0) );
11  
12          FuelNeedsReport report = new FuelNeedsReport();
13          report.generateText(System.out);
14      }
15  }

Abstraktas klases: Scenārijs

FuelNeedsReport kods:

1   public class FuelNeedsReport {
2       public void generateText(PrintStream output) {
3           Company c = Company.getCompany();
4           Vehicle v;
5           double fuel;
6           double total_fuel = 0.0;
7  
8           for ( int i = 0; i < c.getFleetSize(); i++ ) {
9               v = c.getVehicle(i);
10  
11              // Aprēķina ceļojumam nepieciešamo degvielu
12              fuel = v.calcTripDistance() / v.calcFuelEfficency();
13  
14              output.println("Vehicle  " + v.getName() + " needs "
15                                            + fuel + " liters of fuel.");
16              total_fuel += fuel;
17          }
18          output.println("Total fuel needs is " + total_fuel + " liters.");
19      }
20  }

Abstraktas klases: Risinājums

Abstrakta klase modelē objektu klasi, kurai pilna implementācija nav zināma; toties pilna implementācija ir abstraktās klases konkrētajām apakšklasēm.

Abstraktu klašu attēlošana objektu diagrammā

Abstraktas klases: Risinājums

1  public abstract class Vehicle {
2      public abstract double calcFuelEfficiency();
3      public abstract double calcTripDistance();
4  }

1  public class Truck extends Vehicle {
2      public Truck(double max_load) {...}
3  
4      public double calcFuelEfficiency() {
5          /* calculate the fuel consumption of a truck at a given load */
6      }
7      public double calcTripDistrance() {
8          /* calculate the distance of this trip on highway */
9      }
10 }

1  public class RiverBarge extends Vehicle {
2      public RiverBarge(double max_load) {...}
3  
4      public double calcFuelEfficiency() {
5          /* calculate the fuel efficiency of a river barge */
6      }
7      public double calcTripDistrance() {
8          /* calculate the distance of this trip along the river-ways */
9      }
10 }

Projektēšanas šablons: "Template Method" jeb "Metodes Prototips"

Šablona 'Metodes Prototips' konkrēta ilustrācija

Interfeisi

  • Publisks interfeiss ir kontrakts starp klienta programmu un klasi, kura implementē šo interfeisu.
  • Javā intefeissi (interface) ir šāda kontrakta deklarācija, kur minēti metožu prototipi bez implementācijām.
  • Daudzas mantošanas ziņā nesaistītas klases var implementēt to pašu interfeisu.
  • Klase var implementēt vairākus nesaistītus interfeisus.
  • Javas klases sintakse:
<class_declaration> ::=
<modifier> class <name>  [extends <superclass>]
    [implements <interface> [,<interface>]* ] {
        <declarations>*
}

Interfeisa piemērs

Interfeisa apzīmēšana UML klašu diagrammā
public interface Flyer {
    public void takeOff();
    public void land();
    public void fly();
}

public class Airplane implements Flyer {
    public void takeOff() {
        // accelerate until lift-off
        // raise landing gear
    }
    public void land() {
        // lower landing gear
        // deccelerate and lower flaps until touch-down
        // apply breaks
    }
    public void fly() {
        // keep those engines running
    }
}

Interfeisa piemērs

Klases, kuras implementē vienu interfeisu

Intefeisa piemērs

Mantošanas hierarhija kopā ar interfeisiem

Intefeisa piemērs

public class Bird extends Animal implements Flyer {
    public void takeOff()     { /* take-off implementation   */ }
    public void land()           { /* landing implementation     */ }
    public void fly()             { /* fly implementation             */ }
    public void buildNest() { /* nest building behavior     */ }
    public void layEggs()     { /* egg laying behavior           */ }
    public void eat()             { /* override eating behavior */ }
}

Interfeisa piemērs

Interfeiss 'Flyer' palīdz polimorfiski lietot klasi 'Superman' līdz ar citām

Interfeisa piemērs

public class Airport {
    public static void main(String[] args) {
        Airport metropolisAirport = new Airport();
        Helicopter copter = new Helicopter();
        SeaPlane sPlane = new SeaPlane();
        Flyer S = Superman.getSuperman(); // Superman is a Singleton

        metropolisAirport.givePermissionToLand(copter);
        metropolisAirport.givePermissionToLand(sPlane);
        metropolisAirport.givePermissionToLand(S);
    }

    private void givePermissionToLand(Flyer f) {
        f.land();
    }
}

Vairāku interfeisu piemērs

Diagramma ar vairākiem interfeisiem

Vairāku interfeisu piemērs

public class Harbor {
    public static void main(String[] args) {
        Harbor bostonHarbor = new Harbor();
        RiverBarge barge = new RiverBarge();
        SeaPlane sPlane = new SeaPlane();

        bostonHarbor.givePermissionToDock(barge);
        bostonHarbor.givePermissionToDock(sPlane);
    }

    private void givePermissionToDock(Sailer s) {
        s.dock();
    }
}

Interfeisu lietojumi

  • Vienkopus deklarēt metodes, kuras jāimplementē vienai vai vairākām klasēm
  • Izpaust objekta atbalstīto interfeisu, neizpaužot objekta klasi
  • Definēt līdzīgu uzvedību klasēm, neieviešot starp tām mantošanas attiecības
  • Veikt "daudzkāršo mantošanu", deklarējot klasi, kura implementē vairākus interfeisus

Projektēšanas šablons "Composite"

  • Ļauj klientam manipulēt gan ar atomāru vienību, gan ar šādu vienību kolekciju, izmantojot to pašu interfeisu.
  • Klientam nav jāveido speciāls kods priekš augstāka līmeņa agregācijobjekta, salīdzinot ar pamatobjektu, tās pašas operācijas darbojas uz abiem
  • Risinājums: visi komponenti apmierina to pašu interfeisu
  • "Composite" šablona piemērs: ja failus un direktorijas var kopēt, pārvietot un dzēst ar to pašu komandu
public interface ContentComponent {
    public void printAsHTML(boolean header);
}

Par projektēšanas šabloniem vispār

  • Standarts risinājums izplatītai situācijai
  • Jau iepriekš programmu padara elastīgāku nākotnes izmaiņām
  • Augsta līmeņa programmēšanas idioma (līdzīgi 3-kārtējam atkārtojumam dažos folkloras žanros)
  • Īss nosaukums konkētai programmatūras organizācijas vienībai
  • Noteikts UML objektu diagrammas fragments
  • To ieviešanā reālos projektos jāseko optimizācijas likumam "Don't do it (yet)"
  • Liela daļa aprakstīti "četru bandas" (Gang of Four) grāmatā: Design Patterns: Elements of Reusable Object-Oriented Software; autori - E. Gamma, R. Helm, R. Johnson, J. Vlissides, 1995.

Iekšējās klases

  • Pievienotas sākot ar JavaTM Development Kit (JDKTM) 1.1
  • Ļauj klases definīciju ievietot citā klases definīcijā
  • Ļauj grupēt klases, kuras ir loģiski saderīgas, vai ieviest klases, kurām ir nozīme tikai dotās klases implementācijas ietvaros.
  • Iekšējām klasēm ir piekļuves tiesības savas ietverošās klases redzamības apgabalam.

Iekšējo klašu piemērs

1  public class Outer1 {
2      private int size;
3  
4      /* Declare an inner class called "Inner" */
5      public class Inner {
6          public void doStuff() {
7              // The inner class has access to 'size' from Outer
8              size++;
9          }
10      }
11  
12      public void testTheInner() {
13          Inner i = new Inner();
14          i.doStuff();
15      }
16  }

Iekšējo klašu piemērs

1  public class Outer2 {
2      private int size;
3  
4      public class Inner {
5          public void doStuff() {
6              size++;
7          }
8      }
9  }

1  public class TestInner {
2      public static void main(String[] args) {
3          Outer2 outer = new Outer2();
4  
5          // Must create an Inner object relative to an Outer
6          Outer2.Inner inner = outer.new Inner();
7          inner.doStuff();
8      }
9  }

Iekšējo klašu piemērs

1   public class Outer3 {
2       private int size;
3  
4       public class Inner {
5           private int size;
6  
7           public void doStuff(int size) {
8               size++;                // the local parameter
9               this.size++;           // the Inner object attribute
10              Outer3.this.size++;    // the Outer3 object attribute
11          }
12      }
13  }

Iekšējo klašu piemērs

1  public class Outer4 {
2      private int size = 5;
3  
4      public Object makeTheInner(int localVar) {
5          final int finalLocalVar = 6;
6  
7          // Declare a class within a method!?!
8          class Inner {
9              public String toString() {
10                  return ("#<Inner size=" + size +
11                                  // " localVar=" + localVar + // ERROR: ILLEGAL
12                                  "finalLocalVar=" + finalLocalVar + ">");
13             }
14         }
15  
16         return new Inner();
17     }
18  
19     public static void main(String[] args) {
20         Outer4 outer = new Outer4();
21         Object obj = outer.makeTheInner(47);
22         System.out.println("The object is " + obj);
23     }
24 }

Iekšējo klašu īpašības

  • Varat lietot klases vārdu tikai redzamības apgabalā, citādi jālieto pilns vārds. Iekšējās klases vārdam jāatšķiras no ietverošās klases vārda.
  • Iekšējās klases var definēt arī metodes iekšienē. Tās var piekļūt tikai tiem ietverošās klases mainīgajiem, kuri deklarēti ar final.

Iekšējo klašu īpašības

  • Iekšējās klases var izmantot no ietverošās klases gan statiskos, gan instanču mainīgos, kā arī savu deklarāciju iekļaujošajos blokos definētos konstantos lokālos mainīgos.
  • Iekšējās klases var būt abstraktas.
  • Iekšējām klasēm var būt jebkurš piekļuves veids.
  • Iekšējā klase var būt interfeiss, kuru implementē cita iekšējā klase.

Iekšējo klašu īpašības

  • Iekšējās klases, kuras ir deklarētas ar modificētāju static kļūst ekvivalentas patstāvīgām (top-level) klasēm.
  • Iekšējās klases nevar deklarēt statiskos locekļus, tikai patstāvīgas klases var deklarēt statiskus locekļus
  • Iekšējai klase, kura grib lietot statisku locekli, pašai jābūt statiskai (???).

Jautājumi paškontrolei

  • Raksturot statiskos mainīgos, metodes un statiskos inicializācijas blokus
  • Raksturot konstantas klases, metodes un mainīgos
  • Izskaidrot, kā un kad lietot abstraktas klases un metodes
  • Izskaidrot, kā un kad lietot iekšējās klases
  • Atšķirt statiskas un nestatiskas iekšējās klases
  • Izskaidrot, kā un kad lietot interfeisus

    Javas programmā prast atrast:

  • Statiskas metodes un atribūtus
  • Konstantas metodes un atribūtus
  • Iekšējās klases
  • Interfeisus un abstraktas klases
  • Abstraktas metodes

    Diskusiju tēmas

  • Ar ko klases un tās iekšējo klašu izveidošana pēc redzamības apgabaliem, sintaktiskā pieraksta, utml. atšķiras no līdzīga risinājuma - daudzu atsevišķu klašu izveidošanas atsevišķā apakšpakotnē?
  • Kāpēc metožu līmenī definētas iekšējas klases var redzēt ietverošajā klasē tikai konstantos mainīgos? Ko darīt, ja vajag piekļūt nekonstantiem mainīgajiem attiecīgajā metodē vai klasē?
  • Kāds statiskam mainīgajam var būt redzamības apgabals? (visa klase, metode?, bloks?)