JAVA PAGRINDAI DETALIAU

Java konstruktorių perkrovimas

Konstruktorių perkrovimas leidžia tai pačiai klasei turėti kelis konstruktorius su skirtingais parametrų sąrašais. Todėl objektą galima sukurti pateikiant tik tuo metu žinomus duomenis.

Kam reikia kelių konstruktorių?

Įsivaizduok siuntų registravimo programą. Kartais dar nežinome nieko, kartais žinome tik gavėją, o kartais turime visus siuntos duomenis. Vietoje trijų skirtingų klasių galime pateikti tris tos pačios klasės sukūrimo būdus.

class Siunta {
    String gavejas;
    double svoris;
    boolean skubi;

    Siunta() {
        gavejas = "Nenurodytas";
        svoris = 0.0;
        skubi = false;
    }

    Siunta(String gavejas) {
        this.gavejas = gavejas;
        svoris = 0.0;
        skubi = false;
    }

    Siunta(String gavejas, double svoris, boolean skubi) {
        this.gavejas = gavejas;
        this.svoris = svoris;
        this.skubi = skubi;
    }

    void rodyti() {
        System.out.println(gavejas + ", " + svoris + " kg, skubi: " + skubi);
    }
}

Siunta neuzpildyta = new Siunta();
Siunta laiskas = new Siunta("Ieva");
Siunta deze = new Siunta("Mantas", 4.5, true);

neuzpildyta.rodyti();
laiskas.rodyti();
deze.rodyti();
Konsolės rezultatas

Nenurodytas, 0.0 kg, skubi: false
Ieva, 0.0 kg, skubi: false
Mantas, 4.5 kg, skubi: true

  1. new Siunta() neturi argumentų, todėl Java pasirenka konstruktorių be parametrų.
  2. new Siunta("Ieva") turi vieną String argumentą, todėl pasirenkamas antras konstruktorius.
  3. Trečiame iškvietime perduodami String, double ir boolean, todėl tinka trečias konstruktorius.
  4. Pasirinkimas atliekamas kompiliavimo metu pagal argumentų skaičių, tipus ir jų tvarką.

Kuo konstruktoriai turi skirtis?

class Bilietas {
    Bilietas(String renginys) { }
    Bilietas(String renginys, int vieta) { }
    Bilietas(int vieta, String renginys) { }
}
Konsolės rezultato nėra.

Kodas tik aprašo tris leidžiamus Bilietas objektų kūrimo būdus.

Konstruktoriaus parašą lemia parametrų:

  • skaičius;
  • tipai;
  • tipų tvarka.

Parametrų pavadinimai parašo nekeičia. Taip pat konstruktorius neturi grąžinamojo tipo, todėl juo konstruktorių atskirti negalima.

Bendrą darbą perduok su this(...)

Pirmajame pavyzdyje kai kurios pradinės reikšmės kartojosi. Patogiau turėti vieną pagrindinį konstruktorių, o trumpesnius konstruktorius nukreipti į jį:

class Puodelis {
    String spalva;
    int talpaMl;

    Puodelis() {
        this("baltas", 300);
    }

    Puodelis(String spalva) {
        this(spalva, 300);
    }

    Puodelis(String spalva, int talpaMl) {
        this.spalva = spalva;
        this.talpaMl = talpaMl;
    }
}

Puodelis pirmas = new Puodelis();
Puodelis antras = new Puodelis("mėlynas");

System.out.println(pirmas.spalva + " " + pirmas.talpaMl);
System.out.println(antras.spalva + " " + antras.talpaMl);
Konsolės rezultatas

baltas 300
mėlynas 300

  1. Konstruktorius be parametrų pasirenka numatytąją spalvą ir talpą.
  2. Konstruktorius su spalva perduoda ją pagrindiniam konstruktoriui, o talpai naudoja 300.
  3. Pagrindinis dviejų parametrų konstruktorius vienoje vietoje priskiria abu atributus.
  4. this(...) privalo būti pati pirmoji konstruktoriaus instrukcija.

Plačiau apie Java this raktažodį

Parašius konstruktorių, automatinis tuščias konstruktorius dingsta

class Knyga {
    String pavadinimas;

    Knyga(String pavadinimas) {
        this.pavadinimas = pavadinimas;
    }
}

Knyga pirma = new Knyga("Altorių šešėly");
// Knyga antra = new Knyga(); // Kompiliavimo klaida
Rezultatas

Pirmas objekto sukūrimas veikia. Atkomentavus antrą eilutę, kodas nesikompiliuos, nes klasė neturi konstruktoriaus be parametrų.

Java automatiškai suteikia tuščią konstruktorių tik tada, kai klasėje patys neaprašėme nė vieno konstruktoriaus. Jei norime ir tuščio, ir parametrizuoto varianto, turime aiškiai parašyti abu.

Parametrų pavadinimų pakeisti nepakanka

class Saskaita {
    Saskaita(String savininkas, int numeris) { }

    // Klaida: toks pats konstruktoriaus parašas
    Saskaita(String vardas, int kodas) { }
}
Rezultatas

Kodas nesukompiliuojamas, nes abu konstruktoriai turi tą patį parašą: Saskaita(String, int).

Java žiūri į parametrų tipus ir jų tvarką, o ne į vardus savininkas, vardas, numeris ar kodas.

Venk dviprasmiško iškvietimo

class Pranesimas {
    Pranesimas(String tekstas) { }
    Pranesimas(StringBuilder tekstas) { }
}

// Klaida: null tinka abiem konstruktoriams
Pranesimas pranesimas = new Pranesimas(null);
Rezultatas

Kodas nesukompiliuojamas: null gali tikti ir String, ir StringBuilder, todėl kompiliatorius negali vienareikšmiškai pasirinkti.

Perkrovimai turi būti patogūs žmogui ir aiškūs kompiliatoriui. Jei iškvietimai lengvai supainiojami, prasmingiau naudoti aiškiai pavadintus kūrimo metodus arba kitokią klasės struktūrą.

Konstruktorių ir metodų perkrovimas

Konstruktorių ir metodų perkrovimo skirtumai
SavybėKonstruktoriaiMetodai
PaskirtisParuošti naują objektąAtlikti veiksmą
PavadinimasVisada sutampa su klasės pavadinimuGali būti bet koks tinkamas metodo vardas
Grąžinamasis tipasNerašomasPrivalomas, įskaitant void
IškvietimasKuriant objektą su new arba per this(...)Rašant metodo pavadinimą ir argumentus
PasirinkimasKompiliavimo metu pagal argumentus ir galimus parašus

Plačiau apie Java metodų perkrovimą

Ką verta prisiminti?

  • Perkrauti konstruktoriai suteikia kelis tos pačios klasės objekto sukūrimo būdus.
  • Jie privalo skirtis parametrų skaičiumi, tipais arba tipų tvarka.
  • Kompiliatorius konstruktorių pasirenka pagal perduotus argumentus.
  • this(...) padeda išvengti pasikartojančios inicializavimo logikos.
  • this(...) turi būti pirmoji konstruktoriaus instrukcija, o cikliniai konstruktorių iškvietimai neleidžiami.
  • Aprašius bent vieną konstruktorių, Java nebesukuria automatinio konstruktoriaus be parametrų.