Liigu peamise sisu juurde

final võtmesõna

Sissejuhatus

final märksõna tähistab, et antud element koodis on lõplik ning seda pole võimalik enam muuta. Sõltuvalt asukohast on sellel erinev tähendus, kuid põhimõte on sama.

final võtmesõna on võimalik määrata:

  • Väljadele ja muutujatele (nii koodiplokis kui ka meetodi parameetrites) - antud juhul on väärtus lõplik ning seda ei ole võimalik enam muuta.
  • Meetoditele - Meetodit ei ole võimalik alamklassis üle kirjutada.
  • Klassidele - Antud klassi ei ole võimalik enam laiendada (ehk sellest klassist ei saa alamklassi enam luua).

final võtmesõna väljadel ja muutujatel

Lokaalsed muutujad

Lokaalse muutuja (ja parameetrite) puhul tähistab final, et sellele on võimalik väärtust omistada ainult ühel korral. Uue väärtuse omistamisel tekiks kompileerimisviga:

final int maxAttempts = 3;
System.out.println(maxAttempts); // 3

maxAttempts = 5; // Error: cannot assign a value to final variable 'maxAttempts'

Antud võte on kasulik juhul, kui on vajadus märku anda nii kompilaatorile kui ka inimesele, kes koodi loeb, et antud muutujat ei tohiks muuta.

Isendimuutujad/väljad

Välja puhul final tähistab, et sellele muutujale saab väärtust määrata ainult ühel korral: kas koheselt muutuja deklareerimisel või läbi konstruktori.

class Circle {
final double radius;

public Circle(double radius) {
this.radius = radius; // Assigned in constructor - OK
}

public double area() {
return Math.PI * radius * radius;
}
}

public class Main {
public static void main(String[] args) {
Circle c = new Circle(5.0);
System.out.println(c.area()); // 78.53981633974483

c.radius = 10.0; // Error: cannot assign a value to final field 'radius'
}
}
oht

final-iga märgitud väljadele peab väärtuse omistama kas muutuja loomise hetkel või konstruktori kaudu. Selle puudumisel tekib kompileerimisviga:

class Circle {
final double radius; // No assigning here

public Circle() {
// No assigning here as well
}

// Result compilation error: Field 'radius' might not have been initialized
}

Antud võtet kasutatakse sarnaselt eelnevale selleks, et märku anda, et antud omadus ei tohiks peale omistamist muutuda. Lisaks on see kasulik selliste omaduste kirjeldamiseks, mis antud objektil kindlalt peaksid olema, näiteks kasutaja ID või arvenumber.

hoiatus

Objekt-väärtuste puhul on oluline märkida, et final ei muuda objekti muutumatuks ehk ei takista objekti oleku muutmist. See ainult takistab antud muutujale uue väärtuse omistamist.
Näiteks:

class Student {
String name;
int age;

public Student(String name, int age) {
this.name = name;
this.age = age;
}
}

public class Main {
public static void main(String[] args) {
final Student student = new Student("Alice", 20);

student.name = "Bob"; // OK - modifying the object's state is allowed
student.age = 21; // OK

student = new Student("Charlie", 22); // Error: cannot reassign a final variable
}
}

Teisisõnu, final tähendab, et antud muutuja peab alati osutama sellele samale objektile. Sisu puhul selline reegel ei kehti.

Objekti muutumatuks tegemiseks peab ka objekti enda klass olema vastavalt disainitud (nt kõik väljad final, puuduvad setter meetodid jne).

Konstantsed väärtused

Üks enim-levinud final märksõna kasutusi on konstantsete väärtuste loomine koos static märksõnaga. Teisisõnu väärtuste tekitamiseks, mis on kõikide objektide puhul samad ning ei muutu kunagi.
Näiteks:

public class InvoiceCalculator {

// Constants instead of "magic numbers"
// Instead of writing the numbers into methods directly,
// we give then descriptive names, making code more understandable
private static final double VAT_RATE = 0.20; // 20% VAT
private static final double DISCOUNT_THRESHOLD = 100; // Discount applies over 100€
private static final double DISCOUNT_RATE = 0.10; // 10% discount

public double calculateTotal(double baseAmount) {
double amountWithVat = baseAmount * (1 + VAT_RATE);

if (amountWithVat > DISCOUNT_THRESHOLD) {
amountWithVat = amountWithVat * (1 - DISCOUNT_RATE);
}

return amountWithVat;
}
}

Konventsiooni järgi (kindlaks tehtud head tavad), konstante (static final välju) nimetatakse UPPER_SNAKE_CASE stiilis. Javas endas puutud kokku selle mustriga näiteks Integer klassis (Integer.MAX_VALUE) või Math klassis (Math.PI).

Konstantide ja sellega seonduvate mustrite/reeglitega saate lähemalt tutvuda siit.

final meetodid

final märksõnaga meetod tähistab, et seda ei saa üle kirjutada (@Override) alamklassi poolt:

class Vehicle {
private String registrationNumber;

public Vehicle(String registrationNumber) {
this.registrationNumber = registrationNumber;
}

// This method cannot be overridden - every vehicle must use the same logic
public final String getRegistrationNumber() {
return registrationNumber;
}

// This method can be overridden
public String describe() {
return "Vehicle " + registrationNumber;
}
}

class Car extends Vehicle {
public Car(String registrationNumber) {
super(registrationNumber);
}

// OK - describe() is not final
@Override
public String describe() {
return "Car " + getRegistrationNumber();
}

// Error: getRegistrationNumber() is final and cannot be overridden
// @Override
// public String getRegistrationNumber() { ... }
}

See on kasulik, kui mingi meetodi loogika peab olema ühtne üle kõikide implementatsioonide. Näiteks võib tuua ranged äriloogikad või turvanõuded.

final klass

final märksõna klassi puhul tähistab, et antud klassi pole võimalik enam edasi laiendada ehk sellest klassist ei saa alamklasse luua.

public final class ImmutablePoint {
private final double x;
private final double y;

public ImmutablePoint(double x, double y) {
this.x = x;
this.y = y;
}

public double getX() { return x; }
public double getY() { return y; }
}

class ExtendedPoint extends ImmutablePoint { // Error: cannot inherit from final class 'ImmutablePoint'
// ...
}

Javast endast võib näiteks tuua String klassi. String on final klass ehk programmeerija ei saa luua sellest alamklasse. See tagab, et kõik String objektid käituvad samal etteaimataval viisil.