Programmeren in Java/Package
Hoe meer je programmeert, hoe groter je programma wordt, en hoe groter je programma wordt hoe meer klassen je schrijft. Na een tijd ga je zien dat je véél klassen hebt en dat het onoverzichtelijk wordt. Grote programma's kunnen honderden of zelfs duizenden klassen hebben. Daarnaast hebben die klassen misschien weinig met elkaar te maken. Je hebt misschien klassen zoals Car en Bus die wel gelijkaardig zijn, maar daarnaast heb je misschien ook klassen genaamd Animal, Dog en Cat of nog abstractere klassen zoals DatabaseConnection en die hebben dan weinig met elkaar te maken. Dan wil je graag op één of andere manier overzichtelijk je klassen kunnen bijhouden. Dit kun je in Java doen, net zoals alle bestanden op je computer, door ze sorteren in mappen. In Java worden die mappen echter packages genoemd.
Package declareren
[bewerken]Stel je hebt volgende klasse Car.
public class Car {
// Je programmacode voor een Car.
}
Je wilt deze voortaan bewaren in een map die specifiek is bedoeld voor voertuigen. Dan kun je in je project een map vehicles aanmaken en je bewaart of verplaatst deze klasse in die map. Dan kun je de package declareren door het sleutelwoord package te gebruiken bovenaan je klasse en de package-naam te vermelden, de package-naam is de naam van de map die je hebt aangemaakt.
package vehicles;
public class Car {
// Je programmacode voor een Car.
}
Als je een goede IDE hebt en je maakt een klasse aan of verplaatst een bestaande klasse in een map, dan zal die zelf de package declareren. Nu heb je een package vehicles die een handvol voertuigen bevat zoals Car, Bus, Bicycle en Airplane. Maar je programma wordt steeds groter en groter, en heb je na een tijd tientallen of honderden klassen die in de package vehicles thuishoren en het is niet langer overzichtelijk. Gelukkig kun je net zoals mappen waarin je submappen kunt maken bij packages ook subpackages maken. Je maakt daarvoor simpelweg een extra map aan in je vehicles, bijvoorbeeld motorvehicles en je verplaatst je klassen die daar thuishoren in en pas je je package declaratie aan als volgt.
package vehicles.motorvehicles;
public class Car {
// Je programmacode voor een Car.
}
Opnieuw als je een goede IDE hebt zal de IDE het voor jou aanpassen. Je kunt zoveel packages aanmaken als je maar wilt, maar hoe het voor jezelf liefst zo logisch mogelijk.
Een klasse van een package importen
[bewerken]Nu dat je Car klasse in de package genaamd vehicles.motorvehicles zit, wil je die gebruiken in je Main-klasse die in je hoofdmap bevindt.
public class Main {
public static void main(String[] args) {
Car car = new Car();
}
}
Als je dit zou compileren krijg je wellicht een fout zoals deze:
java: cannot find symbol symbol: class Car location: class Main
Zodra je klassen begint onder te verdelen in packages, moet je er rekening mee houden dat een klasse enkel de andere klassen kunnen zien die in zich in dezelfde package of map bevinden. Aangezien de klasse Car zich in de package vehicles.motorvehicle kan de Main klasse die niet zien. Je moet daarom de klasse Car importeren, dit kun je doen door het sleutelwoord import te gebruiken gevolgd door de package-naam met daarbij ook de naam van de klasse.
import vehicles.motorvehicles.Car;
public class Main {
public static void main(String[] args) {
Car car = new Car();
}
}
Nu kun je de Main klasse zonder problemen compileren. Als je een goeie IDE hebt zal deze meestal wel meteen een foutmelding geven wanneer je een klasse gebruikt die nog niet geïmporteerd is en vaak geven ze dan ook de optie om meteen de klasse te importeren. Heb je nog klassen te importeren kun je simpelweg een extra regel toevoegen per klasse.
import vehicles.motorvehicles.Car;
import vehicles.motorvehicles.Bus;
import vehicles.Bicycle;
Een package importen met een wildcard
[bewerken]Je het nu een package genaamd vehicles.motorvehicles en deze bevat de klassen Car, Bus, Motorcycle. Je kunt ze dan één voor één importeren als volgt.
import vehicles.motorvehicles.Car;
import vehicles.motorvehicles.Bus;
import vehicles.motorvehicles.Motorcycle;
public class Main {
public static void main(String[] args) {
// Je programmacode.
}
}
Op zich geen probleem, maar wat als je 20 klassen van dezelfde package moet importeren, dan gaan je 20 import statements moeten gebruiken. Als je alle klassen van een bepaalde package nodig hebt kun je de zogenaamde wildcard gebruiken. Een wildcard is een speciaal teken dat gebruikt word om een iets anders te vervangen. Bij de import-statement kunnen we de asterisk (*) gebruiken en deze zal door Java geïnterpreteerd worden als de vraag om alle klassen van een package te importeren. Je gebruikt een wildcard als volgt.
import vehicles.motorvehicles.*;
public class Main {
public static void main(String[] args) {
// Je programmacode.
}
}
Je vermeld dus de package-naam gevolgd door de asterisk, dit zal dan alle klassen van die package importeren. Let wel enkel van deze package, niet van eventuele subpackages. Ook wordt het afgeraden om dit te gebruiken als je niet alle klassen van een package nodig hebt.
Fully qualified name
[bewerken]Nu dat je klassen in packages doet, krijgen ze wat ze een fully qualified name noemen. Deze fully qualified name bestaat uit de package-naam en de naam van de klasse. Je kunt dit makkelijk krijgen door het volgende te doen.
import vehicles.motorvehicles.Car;
public class Main {
public static void main(String[] args) {
String fullyQualifiedName = Car.class.getName();
System.out.println(fullyQualifiedName);
}
}
Dan krijg je volgend uitvoer.
vehicles.motorvehicles.Car
Dit is natuurlijk hetzelfde als wat je vermeldt bij de import statement.
Meerdere klassen met dezelfde naam gebruiken
[bewerken]Het kan voorkomen dat je twee klassen moet gebruiken met dezelfde naam.
import vehicles.motorvehicles.Bus;
import computer.Bus;
public class Main {
public static void main(String[] args) {
Bus bus = new Bus();
Bus computer = new Bus();
}
}
Als je dit zou compileren zou je volgende fout kunnen krijgen:
java: a type with the same simple name is already defined by the single-type-import of vehicles.motorvehicles.Bus
In deze fout wordt gezegd dat er al een klasse geïmporteerd is geweest met de naam Bus, en dat je dus niet nog eens een klasse met dezelfde naam kunt importeren. En dan kun je ook volgende fout krijgen.
java: reference to Bus is ambiguous both class vehicles.motorvehicles.Bus in vehicles.motorvehicles and class computer.Bus in computer match
Hier wordt gezegd dat wanneer je bijvoorbeeld new Bus() doet dat de compiler niet weet welke Bus te nemen.
De eerste fout kun je eventueel oplossen door de wildcard te gebruiken in beide import statements, zoals volgt.
import vehicles.motorvehicles.*;
import computer.*;
public class Main {
public static void main(String[] args) {
Bus bus = new Bus();
Bus computer = new Bus();
}
}
Dit zorgt ervoor dat je niet expliciet de twee Bus klassen importeert, maar dit zorgt er niet voor dat Java weet welke Bus klasse er moet worden gebruikt. Om dit te verhelpen kun je de fully qualified name gebruiken in je code om een link te leggen met de juiste klasse.
public class Main {
public static void main(String[] args) {
vehicles.motorvehicles.Bus bus1 = new vehicles.motorvehicles.Bus();
computer.Bus bus2 = new computer.Bus();
}
}
Merk op dat de import-statements verwijderd zijn, dit is geen fout, als je de fully qualified name gebruikt ben je niet verplicht om import statements te gebruiken. Je moet echter nu altijd de fully qualified name gebruiken als je wilt verwijzen naar één van de twee Bus-klassen. Wat je wellicht beter kunt doen is om de meest gebruikte Bus-klasse van de twee expliciet te importeren en dan enkel de fully qualified name te gebruiken als je naar de andere Bus-klasse wilt verwijzen.
import vehicles.motorvehicles.Bus;
public class Main {
public static void main(String[] args) {
Bus bus1 = new Bus();
computer.Bus2 bus = new computer.Bus();
}
}
Naamgeving
[bewerken]Aan de hand van het vorige stukje zie je dat het mogelijk is dat er klassen zijn met de zelfde naam. Dit kan ook gebeuren met package-namen. Daarom zijn er conventies bedacht om namen te geven aan packages en problemen zoals dit te voorkomen:
- Bedrijven of organisaties moeten hun internetdomein in de omgekeerde richting gebruiken als prefix voor namen van hun packages. Bijvoorbeeld "eu.mycompany.mypackage" is een voorbeeld van een package me de naam "mypackage" van het bedrijf "mycompany" met als internetdomein "mycompany.eu".
- Zijn er naamconflicten binnen één bedrijf of organisatie, moeten ze dit afhandelen via hun interne conventies. Als het bedrijf ontwikkelaars heeft in zowel België als Nederland, kunnen ze bijvoorbeeld landcodes toevoegen aan de prefix van hun packages zoals "eu.mycompany.be.mypackage" en "eu.mycompany.nl.mypackage".
- Een domeinnaam mag niet beginnen met een getal, een koppelteken bevatten en een package mag ook geen Java-sleutelwoord zijn. Je kunt dan een underscore gebruiken om dit op te lossen. Bijvoorbeeld:
- "int.com" wordt package "com._int"
- "123companyname.com" wordt package "com._123companyname"
- "company-name.com" wordt package "com.company_name"
- Namen van packages zijn altijd in kleine letters geschreven, dit om geen conflicten te hebben met bijvoorbeeld namen van klassen.
- Packages van Java zelf beginnen met java. of javax. en mogen dus niet door anderen gebruikt worden.