Programmeren in Java/Getters en Setters
Over het algemeen is het sterk aangeraden om eigenschappen of velden van klassen te beschermen door de access modifier private
te gebruiken. Dit zorgt ervoor dat enkel de klasse zelf kan bepalen of code buiten de klasse toegang heeft tot die informatie en indien wel wat er met deze informatie mag gebeuren. Hier komen getters en setters aan te pas. Getters en setters zijn methodes die toelaten om gegevens van een object respectievelijk op te halen en in te stellen. Niet alleen dat, maar getters en setters kunnen gegevensuitwisseling veiliger maken. Ze kunnen ervoor zorgen dat als er informatie wordt weggeschreven naar de klasse, dat er bijvoorbeeld eerst gecontroleerd wordt of de waarde wel geldig is.
Enkele voorbeelden
bewerkenAls voorbeeld gaan we een simpele Person-klasse gebruiken. Deze klasse heeft drie eigenschappen, firstName, lastName en age.
public class Person {
private String firstName;
private String lastName;
private int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
}
Hier willen we dus getters en setters voor hebben. Je maakt ze dan best aan in dezelfde volgorde als de volgorde dat je je velden hebt gedefiniëerd en na de constructor(s).
public class Person {
private String firstName;
private String lastName;
private int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Je bent ook niet verplicht om getters en setters aan te maken voor al je velden. Je kunt zelf bepalen of die al dan niet nodig zijn, bijvoorbeeld je zou kunnen bepalen dat je enkel getters maakt omdat je misschien van mening bent dat de velden niet meer van waarde gaan veranderen. Of misschien voor een veld wel een setter en geen getter of omgekeerd.
Booleans
bewerkenGetters voor booleans krijgen een licht andere naam dan andere getters. In de plaats van het woord get start de methode met het woord is. In onderstaand voorbeeld hebben we een klasse Person met het veld married die bijhoudt of een persoon getrouwd is of niet.
public class Person {
private boolean married = false;
public boolean isMarried() {
return married;
}
public void setMarried(boolean married) {
this.married = married;
}
}
Dus in de plaats van getMarried, krijg je isMarried. Dit dient om onder andere de leesbaarheid van de code te vergroten, als je de method isMarried zou gebruiken in een if krijg je bijvoorbeeld het volgende.
public class Main {
public static void main(String[] args) {
Person person = new Person();
if (person.isMarried()) {
System.out.println("Deze persoon is getrouwd.");
} else {
System.out.println("Deze persoon is niet getrouwd.");
}
}
}
Met if (person.isMarried()) weet je sneller wat de if doet en krijg je ook een logischer klinkende zin dan als je bijvoorbeeld if (person.getMarried()) zou gebruiken.
Arrays of Collections
bewerkenBij arrays, en daarmee ook bij uitbreiding collections, moet je rekening houden met enerzijds de array zelf en anderzijds de waardes in de array. Daarmee wordt bedoeld dat je getters en setters kunt instellen voor de array en ook getters en setters voor de de waardes in de array. We gaan terug onze Person-klasse nemen en we voegen een array voor kinderen van die persoon toe, namelijk children.
public class Person {
private String firstName;
private Person[] children;
public Person(String firstName) {
this.firstName = firstName;
}
public Person(String firstName, Person[] children) {
this.firstName = firstName;
this.children = children;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public Person[] getChildren() {
return children;
}
public void setChildren(Person[] children) {
this.children = children;
}
}
Op zich zijn deze getters en setters hetzelfde als normale getters en setters. Je moet hier wel goed opletten op het feit dat je nu gegevens uitwisselt op basis van referenties. Een array wordt niet gekopieerd als je het meegeeft als een argument of teruggeeft. Je geeft een referentie door en als je een referentie doorgeeft betekent dat je ergens anders in de code ongewenste aanpassingen kunt doen in de array die wordt gebruikt door de klasse Person. Bijvoorbeeld als volgt:
public class Main {
public static void main(String[] args) {
Person father = new Person("Jan", new Person[]{new Person("Klaas"), new Person("Linda")});
System.out.println(father.getFirstName() + " heeft volgende kinderen:");
for (Person child : father.getChildren()) {
System.out.println(child.getFirstName());
}
Person[] children = father.getChildren();
children[0] = null;
children[1] = null;
System.out.println(father.getFirstName() + " heeft volgende kinderen:");
for (Person child : father.getChildren()) {
System.out.println(child.getFirstName());
}
}
}
Dit zal volgende output geven.
Jan heeft volgende kinderen: Klaas Linda Exception in thread "main" java.lang.NullPointerException at Main.main(Main.java:15)
We hebben met father.getChildren() dus niet een kopie van de array opgehaald en in de main bewaard, maar een referentie naar de array in het father-object. Dan hebben we de kinderen in main vervangen door null. Doordat het geen kopie was zijn die aanpassingen doorgevoerd op het origineel en toen we de array een tweede keer ophaalden en probeerde af te printen kregen we opnieuw een referentie naar het origineel. Wanneer we die dan proberen af te printen kregen we een NullPointerException, want in de array zat een null.
Hierbij kunnen getters en setters ook bij helpen. Je zou dan bijvoorbeeld in de getter in de plaats van een referentie naar het origineel, een kopie aanmaken van het origineel en dat doorgeven. Als er dan ongewenste aanpassingen worden gedaan gebeurt dit enkel op de kopie van de array en niet op het origineel.
public Person[] getChildren() {
return children.clone();
}
public void setChildren(Person[] children) {
for (Person child : children) {
if (child == null) {
throw new IllegalArgumentException("Array mag geen null bevatten.");
}
}
this.children = children;
}
Dit zijn maar voorbeelden, misschien zijn deze niet nodig in de gevallen waar jij een getter of setter aanmaakt voor een array of zijn er andere/betere manieren om arrays te beschermen tegen ongewenste aanpassingen.
Naast dus een getter en een setter voor een array, kun je dus ook getters en setters maken voor individuele elementen in die array. Om die methodes aan te duiden worden meestal get en add gebruikt gevolgd door een passelijke naam voor één element. Bijvoorbeeld bij de array children wordt de getter getChild en de setter wordt dan addChild. Daarnaast wil je misschien ook een kind verwijderen uit de array, een methode om een element te verwijderen wordt aangeduid met het woord remove, dus bij een array children wordt dit dan removeChild.
public Person getChild(String firstName) {
// code voor een kind op te zoeken en in de return terug te geven.
}
public void addChild(Person child) {
// code voor een kind toe te voegen aan een array.
}
public void removeChild(Person child) {
// code voor een kind uit een array te halen.
}
Een mogelijke implementatie kan als volgt zijn.
public Person getChild(String firstName) {
for (Person child : children) {
if (child.firstName.equals(firstName)) {
return child;
}
}
return null;
}
public void addChild(Person child) {
children = Arrays.copyOf(children, children.length + 1);
children[children.length - 1] = child;
}
public void removeChild(Person child) {
for (int i = 0; i < children.length; i++) {
if (children[i].equals(child)) {
children[i] = null;
}
}
}
Dit zijn natuurlijk maar voorbeelden en met zulke getters en setters kun je nog variaties maken. Je kunt ook bijvoorbeeld meerdere addChild-setters hebben, zoals een extra addChild die in de plaats van een Person als parametertype heeft gewoon een String nodig heeft voor de voornaam van het kind. Je kunt dan binnen de methode een Person object aanmaken met die voornaam en dan toevoegen aan de array. Je kunt ook het returntype aanpassen. Je kunt bij removeChild in de plaats van void eventueel een boolean terug geven, om aan te geven of een kind is verwijderd (true) of niet (false). Of bij getChild een Optional gebruiken om een NullPointerException te vermijden.
Combinatie van gegevens of berekende gegevens
bewerkenGetters en setters moeten ook niet verplicht één op één gelinkt zijn met een veld. Je kunt een getter maken die gegevens combineert of berekening doet op gegevens van een klasse om dat als resultaat terug te geven. Je kunt ook setters gebruiken om niet één veld aan te passen maar ook meerdere velden, enzoverder. Maar het basisprincipe blijft hetzelfde, getters dienen om gegevens op te halen en setters om gegevens in te stellen.
Stel, je hebt de volgende klasse Person.
public class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Als we nu de volledige naam van iemand willen hebben, dus de voornaam samen met de achternaam, moeten we twee maal een getter oproepen. Namelijk getFirstName en getLastName. Als je dan vaak de volledige naam nodig hebt, heb je véél dubbele code. Je kunt dan een getter toevoegen die een String meegeeft met de volledige naam. Bijvoorbeeld als volgt.
public class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFullName() {
return firstName + " " + lastName;
}
}
Dit werkt natuurlijk ook in de andere richting, je kunt een setter toevoegen voor de voor- en achternaam in één keer te letten.
public void setFullName(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Getters en Setters laten generen door een IDE
bewerkenSommige klassen hebben misschien een paar velden, maar andere een héle hoop. Als je dan voor grote klassen getters en setters moet schrijven, ben je wel eventjes bezig. Als je een IDE gebruikt kun je gelukkig meestal de IDE gebruiken om deze te generen. Vaak is er een optie in een menu om dit te doen. Kijk naar de handleiding of gebruik een zoekmachine om te achterhalen of en hoe je getters en setters kunt laten generen in jouw gekozen IDE.