Ciao! Nelle lezioni precedenti hai già imparato come creare le tue classi complete, con campi e metodi. Questo è un progresso serio, ben fatto! Ma ora devo dirti una spiacevole verità. Non abbiamo creato le nostre lezioni nel modo giusto! Perché? A prima vista, non ci sono errori in questa classe:
Il nome deriva dall'inglese " get " - " ricevere " (ovvero "metodo per ottenere il valore di un campo") e set - " set " (ovvero "metodo per impostare il valore di un campo"). Vediamo come appaiono usando la nostra classe come esempio
Si può fare un’analogia con un telefono cellulare. Immagina che invece di un normale telefono cellulare acceso, ti venga dato un telefono con la custodia aperta, dove si trovano tutti i fili, i circuiti, ecc. sporgente. Il telefono funziona: se ti sforzi e giochi con i diagrammi, potresti anche riuscire a fare una chiamata. Ma probabilmente lo romperai e basta. L'azienda produttrice ti fornisce invece un'interfaccia: il cliente semplicemente compone i numeri richiesti, preme il pulsante verde con la cornetta e inizia la chiamata. E non gli interessa cosa succede all’interno con i circuiti e i fili e come svolgono il loro compito. In questo esempio, l’azienda ha accesso limitato alla parte “interna” (dati) del telefono e ha lasciato all’esterno solo l’interfaccia (metodi). Di conseguenza, il cliente otterrà ciò che voleva (farà una chiamata) e sicuramente non romperà nulla al suo interno.
public class Cat {
public String name;
public int age;
public int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
}
In effetti, c'è. Immagina che mentre sei seduto al lavoro hai scritto una classe come questa Cat
, che denota i gatti. E andò a casa. Mentre eri via, un altro programmatore è venuto a lavorare, ha creato la sua classe Main
, dove ha iniziato a utilizzare la classe che hai scritto Cat
.
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
}
Non importa perché lo ha fatto o come è successo: forse la persona era stanca o non ha dormito abbastanza. Un'altra cosa è importante: la nostra classe attuale Cat
permette di assegnare valori pazzeschi ai campi. Di conseguenza, il programma contiene oggetti con uno stato errato, come questo gatto con un'età di -1000 anni. Che errore abbiamo finito per fare? Quando abbiamo creato la classe, ne abbiamo esposto i dati. I campi name
e age
sono weight
di pubblico dominio. È possibile accedervi ovunque nel programma: basta creare un oggetto Cat
- e il gioco è fatto, qualsiasi programmatore ha accesso ai suoi dati direttamente tramite l'operatore " .
"
Cat cat = new Cat();
cat.name = "";
Qui accediamo direttamente al campo name
e ne impostiamo il valore. Dobbiamo in qualche modo proteggere i nostri dati da interferenze esterne errate. Cosa è necessario per questo? Innanzitutto, tutte le variabili di istanza (campi) devono essere contrassegnate con un modificatore private
. Private è il modificatore di accesso più rigoroso in Java. Se lo usi, i campi della classe Cat
non saranno accessibili al di fuori di essa.
public class Cat {
private String name;
private int age;
private int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";//error! The name field in the Cat class has private access!
}
}
Il compilatore lo vede e produce immediatamente un errore. Adesso i campi sembrano tutelati. Ma si scopre che l’accesso ad essi è “strettamente” chiuso: il programma non può nemmeno misurare il peso di un gatto esistente, se necessario. Anche questa non è un'opzione: in questa forma la nostra classe è praticamente impossibile da utilizzare. Idealmente dobbiamo consentire una sorta di accesso limitato ai dati:
- Altri programmatori dovrebbero essere in grado di creare oggetti
Cat
- Dovrebbero essere in grado di leggere i dati da oggetti già esistenti (ad esempio, ottenere il nome o l'età di un gatto già esistente)
- Dovrebbe anche essere possibile assegnare valori ai campi. Ma allo stesso tempo - solo valori corretti. I nostri oggetti devono essere protetti da quelli errati (no “età = -1000 anni” e simili).
Cat
:
public class Cat {
private String name;
private int age;
private int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
Come puoi vedere, tutto è abbastanza semplice :) I loro nomi sono spesso costituiti dalla parola get/set + il nome del campo di cui sono responsabili. Ad esempio, un metodo getWeight()
restituisce il valore weight
del campo dell'oggetto per il quale è stato chiamato. Questo è quello che appare nel programma:
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
String barsikName = barsik.getName();
int barsikAge = barsik.getAge();
int barsikWeight = barsik.getWeight();
System.out.println("Cat name: " + barsikName);
System.out.println("Cat's age: " + barsikAge);
System.out.println("Weight of the cat: " + barsikWeight);
}
}
Uscita console:
Name кота: Барсик
Возраст кота: 5
Вес кота: 4
Ora da un'altra classe ( Main
) si accede ai campi Cat
, ma solo tramite getter . Tieni presente che i getter hanno un modificatore di accesso public
, il che significa che sono accessibili da qualsiasi punto del programma. E l'assegnazione dei valori? I metodi setter sono responsabili di ciò
public void setName(String name) {
this.name = name;
}
Anche il loro lavoro, come puoi vedere, è semplice. Chiamiamo un metodo setName()
su un oggetto Cat
, gli passiamo una stringa come argomento e questa stringa viene assegnata a un campo name
del nostro oggetto.
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
System.out.println("The original name of the cat is " + barsik.getName());
barsik.setName("Basil");
System.out.println("The new name of the cat -" + barsik.getName());
}
}
Qui abbiamo usato sia getter che setter. Innanzitutto, utilizzando un getter, abbiamo ricevuto e inviato alla console il nome iniziale del gatto. Quindi, utilizzando un setter, name
è stato assegnato un nuovo valore al suo campo: "Vasily". E poi, usando un getter, abbiamo recuperato il nome per verificare se fosse davvero cambiato. Uscita console:
Изначальное Name кота — Барсик
Новое Name кота — Васorй
Sembrerebbe, qual è la differenza? Possiamo anche assegnare valori errati ai campi oggetto, anche se disponiamo di setter:
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
barsik.setAge(-1000);
System.out.println("Age of Barsik -" + barsik.getAge() + " years");
}
}
Uscita console:
Возраст Барсика — -1000 лет
La differenza è che un setter è un metodo a tutti gli effetti . E in un metodo, a differenza di un campo, puoi inserire la logica di verifica necessaria per impedire valori inaccettabili. Ad esempio, puoi facilmente disattivare l'assegnazione di un numero negativo come età:
public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
System.out.println("Error! Age cannot be negative!");
}
}
E ora il nostro codice funziona correttamente!
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
barsik.setAge(-1000);
System.out.println("Age of Barsik -" + barsik.getAge() + " years");
}
}
Uscita console:
Ошибка! Возраст не может быть отрицательным числом!
Возраст Барсика — 5 лет
C'è una restrizione all'interno del setter e protegge dai tentativi di impostare dati errati. L'età di Barsik è rimasta invariata. Getter e setter dovrebbero sempre essere creati. Anche se i tuoi campi non hanno restrizioni sui possibili valori, non ci saranno danni da essi. Immagina una situazione: tu e i tuoi colleghi state scrivendo un programma insieme. Hai creato una classe Cat
con campi pubblici e tutti i programmatori li usano come vogliono. E poi un bel giorno ti viene in mente: “Accidenti, prima o poi qualcuno potrebbe accidentalmente assegnare un numero negativo a una variabile weight
! Dobbiamo creare setter e rendere tutti i campi privati!” Li crei e tutto il codice scritto dai tuoi colleghi si rompe immediatamente. Dopotutto, avevano già scritto un sacco di codice in cui accedevano Cat
direttamente ai campi.
cat.name = "Hippopotamus";
E ora i campi sono diventati privati e il compilatore produce un sacco di errori!
cat.name = "Hippopotamus";//error! The name field of the Cat class has private access!
In una situazione del genere, sarebbe meglio nascondere i campi e creare fin dall'inizio getter-setter . Tutti i tuoi colleghi li userebbero e se ti rendessi conto tardi che hai bisogno di limitare i valori dei campi, aggiungeresti semplicemente un segno di spunta all'interno del setter. E nessuno infrangerebbe il codice già scritto. Naturalmente, se desideri l'accesso in sola lettura a un determinato campo, puoi creare un getter per esso. “Fuori”, cioè fuori dalla classe, dovrebbero essere disponibili solo i metodi. I dati devono essere nascosti.
GO TO FULL VERSION