Konstandid
Sissejuhatus
Konstant on väärtus, mida omistatakse korra ning mida enam muuta ei saa.
Javas konstante luuakse kahe märksõna kombineerimisel, milleks on static ja final.
Nende mõlema märksõnaga olete juba eelnevalt kokku:
finalteeb muutuja lõplikuks ehk sellele pole võimalik uut väärtust omistada.staticseob muutuja klassiga, mitte mõne kindla objektiga.
Nende kahe kooslusel tekib muutuja, mis kuulub klassile ning mida enam muuta ei saa.
Maagiliste arvude probleem
Uurime järgnevat koodi, mille eesmärgiks on välja arvutada:
public class PriceCalculator {
public double calculateTotal(double basePrice) {
double withTax = basePrice * 1.20;
if (withTax > 100) {
withTax = withTax * 0.90;
}
return withTax;
}
}
Kood otseselt töötab, kuid koodi lugedes tekivad järgmised küsimused:
- Mida tähendab arv
1.20? Kas see on käibemaks? Või hoopis mingi poe enda juurdehindlus? - Mida tähendab
withTax > 100sees olev arv? Mille limiit ta täpselt on? - Mida tähendab
0.90? Kas see on 10%-line allahindlus? Mille järgi me seda täpselt määrame?
Neid kirjeldamata arvude nimeks on maagilised arvud ehk arvud, mis esinevad koodis ilma igasugu eelneva kontekstita ning selgituseta, mida need täpselt tähistama peaksid. Maagilised arvud teevad koodi loetavuse ja haldamise raskemaks ning vigade tekkimine on tõenäolisem. Oletame et meil on käibemaksumäär muutub 20% pealt 22% peale, siis peaksime koodibaasist kõik need arvud üles leidma ja ära muutma. Vägagi võimalik, et kuskilt ununeb selle arvu muutmine ära või hoopis ajate mõne muu tähendusega arvuga segamini.
Konstantide loomine
Konstandid lahendavad selle probleemi, määrates väärtustele sisukad nimed:
public class PriceCalculator {
private static final double TAX_RATE = 0.20;
private static final double DISCOUNT_THRESHOLD = 100.0;
private static final double DISCOUNT_RATE = 0.10;
public double calculateTotal(double basePrice) {
double withTax = basePrice * (1 + TAX_RATE);
if (withTax > DISCOUNT_THRESHOLD) {
withTax = withTax * (1 - DISCOUNT_RATE);
}
return withTax;
}
}
Võrreldes eelmise lahendusega on kood palju loetavam:
TAX_RATEon ilmselgelt käibemaksumäärDISCOUNT_THRESHOLDseab piirangu, millisel juhul allahindlust arvestadaDISCOUNT_RATEmäärab ära kui palju soodustust arvutada
Kui tekib vajadus neid väärtusi muuta, siis seda peab ainult ühest kohast muutma.
Nimereeglid
Konstante nimetatakse UPPER_SNAKE_CASE stiilis ehk kõik tähed on suured tähed ning sõnad on eraldatud alakriipsuga (_), Näiteks:
static final int MAX_ATTEMPTS = 3;
static final double PI = 3.14159265358979;
static final String DEFAULT_LANGUAGE = "en";
Antud reegel on laialdaselt tunnustatud ning annab arendajale selgelt märku, et tegemist on konstantse väärtusega. Lisaks olete nendega kokku puutunud ka Javas endas:
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.MIN_VALUE); // -2147483648
System.out.println(Math.PI); // 3.141592653589793
Konstantide asetamine klassis
Reeglina asetatakse konstante klassi algusesse enne konstruktoreid ja meetodeid:
public class GameConfig {
// Constants at the top
public static final int MAX_PLAYERS = 4;
public static final int DEFAULT_LIVES = 3;
public static final int BOARD_WIDTH = 800;
public static final int BOARD_HEIGHT = 600;
// Fields
private int currentPlayers;
private int level;
// Constructor
public GameConfig() {
this.currentPlayers = 0;
this.level = 1;
}
// Methods...
}
Nähtavus
Konstandid võivad olla nii public kui ka private vastavalt sellele, kas nendele peaks ligi pääsema ka klassist väljaspoolt:
public static final- konstant on mõeldud kasutuseks igal poolprivate static final- konstant on seotud ainult klassiga (sisemised väärtused, konfiguratsioon jne.)
Objektid konstantidena
Konstantideks võib määrata ka objekte:
public static final String ERROR_PREFIX = "[ERROR] ";
public static final String DATE_FORMAT = "yyyy-MM-dd";
Antud juhul tuleb meeles pidada, mis mõju omab final objektide peal.
Selleks, et objektid oleksid konstandid peaks nende sisu ka lõplik olema (nt: järjendite puhul tasub kasutada List.of() kaudu loodud järjendeid, kuna neid ei ole võimalik peale loomist muuta).
public static final List<String> VALID_ROLES = List.of("ADMIN", "USER", "GUEST");
VALID_ROLES.add("HACKER"); // Throws UnsupportedOperationException at runtime
Konstantide klass
Vahest on mõistlik konstantide jaoks eraldi klass luua. Seda juhul, kui on väärtusi, mida kasutatakse üle terve projekti ning neid ei ole võimalik kuidagi loogiliselt liigitada.
public class AppConstants {
private AppConstants() {
}
public static final String APP_NAME = "University System";
public static final String APP_VERSION = "1.0.0";
public static final int SESSION_TIMEOUT_SECONDS = 3600;
public static final int MAX_LOGIN_ATTEMPTS = 5;
}
System.out.println("Welcome to " + AppConstants.APP_NAME);
if (attempts > AppConstants.MAX_LOGIN_ATTEMPTS) {
System.out.println("Account locked.");
}
Antud võte järgib utiilklassi põhimõtet ehk sellisel klassil peaks olema privaatne konstruktor ning kõik liikmed peaksid staatilised olema. Samas, seal kus võimalik, tasuks konstant ikkagi liigitada mõne kindla klassi alla.