Diferență între revizuiri ale paginii „Noțiuni avansate de programare obiect-orientată”
(Nu s-au afișat 19 versiuni intermediare efectuate de același utilizator) | |||
Linia 1: | Linia 1: | ||
+ | Conceptele de moștenire, încapsulare și polimorfism sunt concepte de programare orientate obiect, astfel, orice limbaj de acest fel oferă suport pentru ele, nu doar Java. Cu toate acestea, exemplele următoare vor fi realizate în limbajul Java. | ||
+ | |||
== Ierarhii de clase == | == Ierarhii de clase == | ||
Linia 7: | Linia 9: | ||
=== Moștenire === | === Moștenire === | ||
− | Conceptul de moștenire este util atunci când programatorul are nevoie de o anumită funcționalitate care parțial este deja implementată într-o alta clasă. Astfel, acele bucăți de program nu mai trebuie reimplementate și testate ci doar completate. | + | Conceptul de moștenire este util atunci când programatorul are nevoie de o anumită funcționalitate care parțial este deja implementată într-o alta clasă. Astfel, acele bucăți de program nu mai trebuie reimplementate și testate ci doar completate. Java oferă suport doar pentru moștenire simplă (a unei singure clase). |
Putem lua ca exemplu o clasă ''Vehicle'': | Putem lua ca exemplu o clasă ''Vehicle'': | ||
Linia 15: | Linia 17: | ||
public static final float MAX_VEHICLE_SPEED = 300; | public static final float MAX_VEHICLE_SPEED = 300; | ||
− | protected float | + | protected float mCurrentSpeed; |
− | private String | + | private String mName; |
− | private String | + | private String mColor; |
− | public Vehicle(String | + | public Vehicle(String name, String color){ |
− | name | + | mName = name; |
− | color | + | mColor = color; |
} | } | ||
− | public void increaseSpeedBy(float | + | public void increaseSpeedBy(float speed){ |
− | if( | + | // for all classes that inherit this method from |
− | + | // class Vehicle, MAX_VEHICLE_SPEED will always be 300 | |
+ | // because even if this field is hidden by extending classes, | ||
+ | // this method, being defined in class Vehicle, will use the | ||
+ | // static field from class Vehicle, which is 300. | ||
+ | if(mCurrentSpeed + speed <= MAX_VEHICLE_SPEED){ | ||
+ | mCurrentSpeed += speed; | ||
} | } | ||
} | } | ||
public String getName(){ | public String getName(){ | ||
− | return | + | return mName; |
} | } | ||
public String getColor(){ | public String getColor(){ | ||
− | return | + | return mColor; |
} | } | ||
public float getCurrentSpeed(){ | public float getCurrentSpeed(){ | ||
− | return | + | return mCurrentSpeed; |
} | } | ||
Linia 51: | Linia 58: | ||
public static final float MAX_VEHICLE_SPEED = 40; | public static final float MAX_VEHICLE_SPEED = 40; | ||
− | private int | + | private int mHorseCount; |
− | public Carriage(String | + | public Carriage(String color, int horseCount){ |
− | super("Carriage", | + | super("Carriage", color); |
− | horseCount | + | mHorseCount = horseCount; |
} | } | ||
public int getHorseCount(){ | public int getHorseCount(){ | ||
− | return | + | return mHorseCount; |
} | } | ||
Linia 71: | Linia 78: | ||
public static final float MAX_VEHICLE_SPEED = 250; | public static final float MAX_VEHICLE_SPEED = 250; | ||
− | private int | + | private int mCylinders; |
− | private String | + | private String mMake; |
− | private String | + | private String mModel; |
− | public | + | public Automobile(String make, String model, String color, int cylinders){ |
− | super("Automobile", | + | super("Automobile", color); |
− | make | + | mMake = make; |
− | model | + | mModel = model; |
− | cylinders | + | mCylinders = cylinders; |
} | } | ||
public int getCylinders(){ | public int getCylinders(){ | ||
− | return | + | return mCylinders; |
} | } | ||
public String getMake(){ | public String getMake(){ | ||
− | return | + | return mMake; |
} | } | ||
public String getModel(){ | public String getModel(){ | ||
− | return | + | return mModel; |
} | } | ||
Linia 103: | Linia 110: | ||
public static final float MAX_VEHICLE_SPEED = 150; | public static final float MAX_VEHICLE_SPEED = 150; | ||
− | private int | + | private int mMaxCargo; |
− | public Truck(String | + | public Truck(String make, String model, String color, int cylinders, int maxCargo){ |
− | super( | + | super(make, model, color, cylinders); |
− | maxCargo | + | mMaxCargo = maxCargo; |
} | } | ||
public int getMaxCargo(){ | public int getMaxCargo(){ | ||
− | return | + | return mMaxCargo; |
} | } | ||
Linia 129: | Linia 136: | ||
− | + | ==== Suprascriere și ascundere ==== | |
Există două noțiuni foarte importante legate de extinderea claselor: suprascriere și ascundere (overriding & hiding): | Există două noțiuni foarte importante legate de extinderea claselor: suprascriere și ascundere (overriding & hiding): | ||
Linia 142: | Linia 149: | ||
=== Polimorfism === | === Polimorfism === | ||
+ | |||
+ | Considerând exemplul de mai sus, polimorfismul se referă la faptul că un obiect de tip ''Truck'' este în același și un obiect de tip ''Automobile'', un obiect de tip ''Vehicle'' și un ''Object''. Astfel sunt permise următoarele construcții: | ||
+ | |||
+ | <syntaxhighlight lang="java"> | ||
+ | public class MainClass{ | ||
+ | |||
+ | public static void main(String _args[]){ | ||
+ | // this is a reference variable of type Truck, referring | ||
+ | // an object of type Truck | ||
+ | Truck _firstTruck = new Truck("Volvo", "Generic", "blue", 8000, 20); | ||
+ | |||
+ | // this is a reference variable of type Vehicle, referring | ||
+ | // an object of type Vehicle | ||
+ | Vehicle _firstVehicle = new Vehicle("Bicycle", "red"); | ||
+ | |||
+ | // since a Truck is also a Vehicle, we can have | ||
+ | // a reference variable of type Vehicle, referring | ||
+ | // an object of type Truck | ||
+ | Vehicle _secondVehicle = new Truck("Scania", "unknown", "green", 7000, 30); | ||
+ | |||
+ | //better yet, we can do this: | ||
+ | Vehicle _vehicles[] = new Vehicle[3]; | ||
+ | _vehicles[0] = _firstTruck; // a truck is a vehicle | ||
+ | _vehicles[1] = _firstVehicle; | ||
+ | _vehicles[2] = _secondVehicle; | ||
+ | |||
+ | for(int i=0; i<_vehicles.length; i++){ | ||
+ | _vehicles[i].increaseSpeedBy(1.1f); | ||
+ | } | ||
+ | |||
+ | // this will NOT work, since an Object is not | ||
+ | // a Vehicle and will generate a compile time error | ||
+ | // | ||
+ | //_vehicles[0] = new Object(); | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Același lucru se poate realiza cu ajutorul interfețelor: | ||
+ | |||
+ | <syntaxhighlight lang="java"> | ||
+ | public interface Colored{ | ||
+ | public String getColor(); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | <syntaxhighlight lang="java"> | ||
+ | // all objects of type Vehicle and up are not Colored | ||
+ | public class Vehicle implements Colored{ | ||
+ | |||
+ | //... same class content as before | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | <syntaxhighlight lang="java"> | ||
+ | public class MainClass{ | ||
+ | |||
+ | public static void main(String _args[]){ | ||
+ | Colored _coloredItems[] = new Colored[3]; | ||
+ | _coloredItems[0] = new Vehicle("Submarine", yellow); | ||
+ | _coloredItems[1] = new Carriage("brown", 6); | ||
+ | _coloredItems[2] = new Automobile("Dacia", "Sandero", "Yellow", 1600); | ||
+ | |||
+ | for(int i=0; i<_coloredItems.length; i++){ | ||
+ | System.out.println("Color of item " + i + " is " + _coloredItems[i].getColor()); | ||
+ | } | ||
+ | |||
+ | // this will fail with a compile time error | ||
+ | // since class Object does not implement | ||
+ | // interface Colored | ||
+ | Colored _coloredItem = new Object(); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==== Casting (upcast & downcast) ==== | ||
+ | |||
+ | Există situații în care compilatorul are nevoie de informații suplimentare pentru a accepta schimbarea referinței pentru un obiect: | ||
+ | <syntaxhighlight lang="java"> | ||
+ | public class MainClass{ | ||
+ | |||
+ | public static readChar(){ | ||
+ | //... reads a char from keyboard | ||
+ | } | ||
+ | |||
+ | public static void main(String _args[]){ | ||
+ | Vehicle _vehicle; | ||
+ | char c = readChar(); | ||
+ | |||
+ | if(c == 'a'){ | ||
+ | _vehicle = new Carriage("green", 2); | ||
+ | }else{ | ||
+ | _vehicle = new Vehicle("Scooter", "red"); | ||
+ | } | ||
+ | |||
+ | // this is called a down cast and it's done | ||
+ | // automatically (cast from Vehicle down to Object): | ||
+ | Object _obj = _vehicle; | ||
+ | // same with: | ||
+ | _obj = (Object)_vehicle; | ||
+ | |||
+ | // the following does NOT work since the compiler does | ||
+ | // not have enough information about the reference | ||
+ | // _vehicle. What it knows for sure is that it refers to | ||
+ | // an object of type Vehicle. It COULD be a Carriage, but | ||
+ | // it doesn't know: | ||
+ | Carriage _carriage = _vehicle; | ||
+ | |||
+ | // so the programmer needs to explicitly tell the compiler | ||
+ | // that the object is a Carriage, and this is called an | ||
+ | // up cast (up from Vehicle to Carriage): | ||
+ | Carriage _carriage = (Carriage)_vehicle; | ||
+ | // however, _vehicle can still NOT be of type Carriage | ||
+ | // (if char c is not 'a'), and then a runtime exception | ||
+ | // of type ClassCastException will be thrown | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | <div class="regula"><font color="#ff0000">Regulă:</font> Up cast trebuie făcut întotdeauna explicit, indiferent cât de clar este tipul obiectului pentru programator:</div> | ||
+ | |||
+ | |||
+ | <syntaxhighlight lang="java"> | ||
+ | String _someString = "Help!"; | ||
+ | Object _obj = _someString; //implicit down cast | ||
+ | String _otherString = (String)_obj; //explicit up cast | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Mai mult despre polimorfism: | ||
+ | * http://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html | ||
== Încapsulare == | == Încapsulare == | ||
+ | |||
+ | Încapsularea se referă la modul de realizare a unei clase astfel încât implementarea să fie transparentă pentru utilizator. Astfel, elementele interne ale clasei sunt protejate la accesul neautorizat (încapsulate) și modificarea implementării nu influențează modul în care clasa este folosită în aplicații complexe. | ||
+ | |||
+ | Ca exemplu pentru noțiunea de încapsulare, vom da ca exemplu o clasă '''Sorter''': | ||
+ | <syntaxhighlight lang="java"> | ||
+ | public class Sorter{ | ||
+ | |||
+ | private Comparable[] elements; | ||
+ | |||
+ | public Sorter(Comparable[] _elements){ | ||
+ | elements = _elements; | ||
+ | } | ||
+ | |||
+ | public Comparable[] sort(){ | ||
+ | return bubbleSort(); | ||
+ | } | ||
+ | |||
+ | private Comparable[] bubbleSort(){ | ||
+ | Comparable[] _result = new Comparable[elements.length]; | ||
+ | System.arraycopy(_result, 0, elements, 0, _result.length); | ||
+ | boolean _done; | ||
+ | do{ | ||
+ | _done = true; | ||
+ | for(int i=0; i<_result.length - 1; i++){ | ||
+ | if(_result[i].compareTo(_result[i + 1]) > 0){ | ||
+ | Comparable _temp = _result[i]; | ||
+ | _result[i] = _result[i + 1]; | ||
+ | _result[i + 1] = _temp; | ||
+ | _done = false; | ||
+ | } | ||
+ | } | ||
+ | }while(!_done); | ||
+ | |||
+ | return _result; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Se observă că singurele metode publice sunt constructorul și metoda ''sort()''. Astfel, implementarea efectivă a algoritmului de sortare este ascunsă. Dacă la un moment dat clasa '''Sorter''' va fi modificată astfel încât să folosească algoritmul Quick Sort în loc de Bubble Sort, orice clasă care folosește clasa '''Sorter''' va continua să funcționeze la fel ca și până atunci. | ||
+ | |||
+ | === Getters & Setters === | ||
+ | |||
+ | Un alt exemplu de încapsulare este practica de implementa pentru fiecare câmp câte două metode numite ''setter'' și ''getter''. Motivul pentru această abordare este faptul că dacă un câmp este declarat public acesta poate fi modificat fără restricții de orice altă clasă. În schimb, dacă acest câmp are restricții, atunci acestea pot fi aplicate doar cu ajutorul unei metode. Vom da ca exemplu o clasă '''Person''': | ||
+ | <syntaxhighlight lang="java"> | ||
+ | public class Person{ | ||
+ | |||
+ | public static final boolean MALE = true; | ||
+ | public static final boolean FEMALE = false; | ||
+ | |||
+ | private String fullName; | ||
+ | private short age; | ||
+ | private boolean gender; | ||
+ | |||
+ | public Person(String _fullName, short _age, boolean _gender){ | ||
+ | setName(_fullName); | ||
+ | setAge(_age); | ||
+ | gender = _gender; | ||
+ | } | ||
+ | |||
+ | // setter method for field fullName | ||
+ | public void setName(String _fullName){ | ||
+ | // make use of regular expressions to check for | ||
+ | // a valid name | ||
+ | if(_fullName.matches("[A-Z][a-zA-Z]+([ \\-][A-Z][a-zA-Z]+)+")){ | ||
+ | fullName = _fullName; | ||
+ | }else{ | ||
+ | throw new RuntimeException("Invalid full name " + _fullName); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // setter method for field age | ||
+ | public void setAge(short _age){ | ||
+ | if(_age >= 0 && _age <= 150){ | ||
+ | age = _age; | ||
+ | }else{ | ||
+ | throw new RuntimeException("Invalid age " + _age); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // setter method for field sex | ||
+ | public void setGender(boolean _gender){ | ||
+ | gender = _gender; | ||
+ | } | ||
+ | |||
+ | // getter method for field fullName | ||
+ | public String getName(){ | ||
+ | return fullName; | ||
+ | } | ||
+ | |||
+ | // getter method for field age | ||
+ | public short getAge(){ | ||
+ | return age; | ||
+ | } | ||
+ | |||
+ | // getter method for field gender | ||
+ | public boolean getGender(){ | ||
+ | return gender; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Se observă că dacă accesul la cele două câmpuri ''fullName'' și ''age'' ar fi fost public, atunci acestea ar fi putut fi modificate cu orice valori invalide pentru scopul lor. Dar în acest fel, metoda ''setter'' verifică valoarea primită ca argument înainte de a fi atribuită câmpului. | ||
+ | |||
+ | <div class="conventie"><font color="#0000ff">Convenție:</font> Câmpurile unei clase se definesc private sau protejate, iar pentru cele se dorește acces public se implementează metode ''getter'' și ''setter'' publice.</div> |
Versiunea curentă din 14 octombrie 2016 08:12
Conceptele de moștenire, încapsulare și polimorfism sunt concepte de programare orientate obiect, astfel, orice limbaj de acest fel oferă suport pentru ele, nu doar Java. Cu toate acestea, exemplele următoare vor fi realizate în limbajul Java.
Ierarhii de clase
Una din cele mai utile facilități ale unui limbaj orientat obiect, din punct de vedere al ușurinței de a citi și menține o aplicație, este aceea de a crea o ierarhie de clase. Particularitățile unei ierarhii de clase sunt:
- o clasă poate extinde o altă clasă, moștenind din funcționalitatea acesteia din urmă;
- un obiect instanțiat dintr-o clasă B care este extinsă dintr-o clasă A, este în același timp de tip B dar și de tip A (polimorfism).
Moștenire
Conceptul de moștenire este util atunci când programatorul are nevoie de o anumită funcționalitate care parțial este deja implementată într-o alta clasă. Astfel, acele bucăți de program nu mai trebuie reimplementate și testate ci doar completate. Java oferă suport doar pentru moștenire simplă (a unei singure clase).
Putem lua ca exemplu o clasă Vehicle:
public class Vehicle{
public static final float MAX_VEHICLE_SPEED = 300;
protected float mCurrentSpeed;
private String mName;
private String mColor;
public Vehicle(String name, String color){
mName = name;
mColor = color;
}
public void increaseSpeedBy(float speed){
// for all classes that inherit this method from
// class Vehicle, MAX_VEHICLE_SPEED will always be 300
// because even if this field is hidden by extending classes,
// this method, being defined in class Vehicle, will use the
// static field from class Vehicle, which is 300.
if(mCurrentSpeed + speed <= MAX_VEHICLE_SPEED){
mCurrentSpeed += speed;
}
}
public String getName(){
return mName;
}
public String getColor(){
return mColor;
}
public float getCurrentSpeed(){
return mCurrentSpeed;
}
}
... și trei clase extinse: Carriage
public class Carriage extends Vehicle{
public static final float MAX_VEHICLE_SPEED = 40;
private int mHorseCount;
public Carriage(String color, int horseCount){
super("Carriage", color);
mHorseCount = horseCount;
}
public int getHorseCount(){
return mHorseCount;
}
}
... Automobile:
public class Automobile extends Vehicle{
public static final float MAX_VEHICLE_SPEED = 250;
private int mCylinders;
private String mMake;
private String mModel;
public Automobile(String make, String model, String color, int cylinders){
super("Automobile", color);
mMake = make;
mModel = model;
mCylinders = cylinders;
}
public int getCylinders(){
return mCylinders;
}
public String getMake(){
return mMake;
}
public String getModel(){
return mModel;
}
}
... și Truck:
public class Truck extends Automobile{
public static final float MAX_VEHICLE_SPEED = 150;
private int mMaxCargo;
public Truck(String make, String model, String color, int cylinders, int maxCargo){
super(make, model, color, cylinders);
mMaxCargo = maxCargo;
}
public int getMaxCargo(){
return mMaxCargo;
}
public String getName(){
return "Truck";
}
}
Schema bloc a ierarhiei este prezentată mai jos:
Suprascriere și ascundere
Există două noțiuni foarte importante legate de extinderea claselor: suprascriere și ascundere (overriding & hiding):
- suprascrierea unei metode se referă la definirea în clasa extinsă a unei metode ne-statice cu aceeași semnătură (prototip) cu o metodă ne-statică din clasa de bază;
- ascunderea unei metode se referă la definirea în clasa extinsă a unei metode statice cu aceeași semnătură (prototip) cu o metodă statică din clasa de bază.
Mai multe despre moștenire și suprascriere/ ascundere pe pagina oficială Oracle:
- http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
- http://docs.oracle.com/javase/tutorial/java/IandI/override.html
Polimorfism
Considerând exemplul de mai sus, polimorfismul se referă la faptul că un obiect de tip Truck este în același și un obiect de tip Automobile, un obiect de tip Vehicle și un Object. Astfel sunt permise următoarele construcții:
public class MainClass{
public static void main(String _args[]){
// this is a reference variable of type Truck, referring
// an object of type Truck
Truck _firstTruck = new Truck("Volvo", "Generic", "blue", 8000, 20);
// this is a reference variable of type Vehicle, referring
// an object of type Vehicle
Vehicle _firstVehicle = new Vehicle("Bicycle", "red");
// since a Truck is also a Vehicle, we can have
// a reference variable of type Vehicle, referring
// an object of type Truck
Vehicle _secondVehicle = new Truck("Scania", "unknown", "green", 7000, 30);
//better yet, we can do this:
Vehicle _vehicles[] = new Vehicle[3];
_vehicles[0] = _firstTruck; // a truck is a vehicle
_vehicles[1] = _firstVehicle;
_vehicles[2] = _secondVehicle;
for(int i=0; i<_vehicles.length; i++){
_vehicles[i].increaseSpeedBy(1.1f);
}
// this will NOT work, since an Object is not
// a Vehicle and will generate a compile time error
//
//_vehicles[0] = new Object();
}
}
Același lucru se poate realiza cu ajutorul interfețelor:
public interface Colored{
public String getColor();
}
// all objects of type Vehicle and up are not Colored
public class Vehicle implements Colored{
//... same class content as before
}
public class MainClass{
public static void main(String _args[]){
Colored _coloredItems[] = new Colored[3];
_coloredItems[0] = new Vehicle("Submarine", yellow);
_coloredItems[1] = new Carriage("brown", 6);
_coloredItems[2] = new Automobile("Dacia", "Sandero", "Yellow", 1600);
for(int i=0; i<_coloredItems.length; i++){
System.out.println("Color of item " + i + " is " + _coloredItems[i].getColor());
}
// this will fail with a compile time error
// since class Object does not implement
// interface Colored
Colored _coloredItem = new Object();
}
}
Casting (upcast & downcast)
Există situații în care compilatorul are nevoie de informații suplimentare pentru a accepta schimbarea referinței pentru un obiect:
public class MainClass{
public static readChar(){
//... reads a char from keyboard
}
public static void main(String _args[]){
Vehicle _vehicle;
char c = readChar();
if(c == 'a'){
_vehicle = new Carriage("green", 2);
}else{
_vehicle = new Vehicle("Scooter", "red");
}
// this is called a down cast and it's done
// automatically (cast from Vehicle down to Object):
Object _obj = _vehicle;
// same with:
_obj = (Object)_vehicle;
// the following does NOT work since the compiler does
// not have enough information about the reference
// _vehicle. What it knows for sure is that it refers to
// an object of type Vehicle. It COULD be a Carriage, but
// it doesn't know:
Carriage _carriage = _vehicle;
// so the programmer needs to explicitly tell the compiler
// that the object is a Carriage, and this is called an
// up cast (up from Vehicle to Carriage):
Carriage _carriage = (Carriage)_vehicle;
// however, _vehicle can still NOT be of type Carriage
// (if char c is not 'a'), and then a runtime exception
// of type ClassCastException will be thrown
}
}
String _someString = "Help!";
Object _obj = _someString; //implicit down cast
String _otherString = (String)_obj; //explicit up cast
Mai mult despre polimorfism:
Încapsulare
Încapsularea se referă la modul de realizare a unei clase astfel încât implementarea să fie transparentă pentru utilizator. Astfel, elementele interne ale clasei sunt protejate la accesul neautorizat (încapsulate) și modificarea implementării nu influențează modul în care clasa este folosită în aplicații complexe.
Ca exemplu pentru noțiunea de încapsulare, vom da ca exemplu o clasă Sorter:
public class Sorter{
private Comparable[] elements;
public Sorter(Comparable[] _elements){
elements = _elements;
}
public Comparable[] sort(){
return bubbleSort();
}
private Comparable[] bubbleSort(){
Comparable[] _result = new Comparable[elements.length];
System.arraycopy(_result, 0, elements, 0, _result.length);
boolean _done;
do{
_done = true;
for(int i=0; i<_result.length - 1; i++){
if(_result[i].compareTo(_result[i + 1]) > 0){
Comparable _temp = _result[i];
_result[i] = _result[i + 1];
_result[i + 1] = _temp;
_done = false;
}
}
}while(!_done);
return _result;
}
}
Se observă că singurele metode publice sunt constructorul și metoda sort(). Astfel, implementarea efectivă a algoritmului de sortare este ascunsă. Dacă la un moment dat clasa Sorter va fi modificată astfel încât să folosească algoritmul Quick Sort în loc de Bubble Sort, orice clasă care folosește clasa Sorter va continua să funcționeze la fel ca și până atunci.
Getters & Setters
Un alt exemplu de încapsulare este practica de implementa pentru fiecare câmp câte două metode numite setter și getter. Motivul pentru această abordare este faptul că dacă un câmp este declarat public acesta poate fi modificat fără restricții de orice altă clasă. În schimb, dacă acest câmp are restricții, atunci acestea pot fi aplicate doar cu ajutorul unei metode. Vom da ca exemplu o clasă Person:
public class Person{
public static final boolean MALE = true;
public static final boolean FEMALE = false;
private String fullName;
private short age;
private boolean gender;
public Person(String _fullName, short _age, boolean _gender){
setName(_fullName);
setAge(_age);
gender = _gender;
}
// setter method for field fullName
public void setName(String _fullName){
// make use of regular expressions to check for
// a valid name
if(_fullName.matches("[A-Z][a-zA-Z]+([ \\-][A-Z][a-zA-Z]+)+")){
fullName = _fullName;
}else{
throw new RuntimeException("Invalid full name " + _fullName);
}
}
// setter method for field age
public void setAge(short _age){
if(_age >= 0 && _age <= 150){
age = _age;
}else{
throw new RuntimeException("Invalid age " + _age);
}
}
// setter method for field sex
public void setGender(boolean _gender){
gender = _gender;
}
// getter method for field fullName
public String getName(){
return fullName;
}
// getter method for field age
public short getAge(){
return age;
}
// getter method for field gender
public boolean getGender(){
return gender;
}
}
Se observă că dacă accesul la cele două câmpuri fullName și age ar fi fost public, atunci acestea ar fi putut fi modificate cu orice valori invalide pentru scopul lor. Dar în acest fel, metoda setter verifică valoarea primită ca argument înainte de a fi atribuită câmpului.