Abstracte klassen in Java

1. Overzicht

Er zijn veel gevallen bij de uitvoering van een contract waarbij we sommige delen van de uitvoering willen uitstellen om later af te ronden. We kunnen dit gemakkelijk bereiken in Java door middel van abstracte klassen.

In deze tutorial leren we de basisprincipes van abstracte klassen in Java, en in welke gevallen ze nuttig kunnen zijn.

2. Sleutelconcepten voor abstracte lessen

Voordat je ingaat op wanneer je een abstracte les moet gebruiken, laten we eens kijken naar hun meest relevante kenmerken:

  • We definiëren een abstracte klasse met de abstract modifier voorafgaand aan de klasse trefwoord
  • Een abstracte klasse kan worden onderverdeeld, maar kan niet worden geïnstantieerd
  • Als een klasse een of meer abstract methoden, dan moet de klasse zelf worden gedeclareerd abstract
  • Een abstracte klasse kan zowel abstracte als concrete methoden declareren
  • Een subklasse die is afgeleid van een abstracte klasse, moet alle abstracte methoden van de basisklasse implementeren of zelf abstract zijn

Om deze concepten beter te begrijpen, zullen we een eenvoudig voorbeeld maken.

Laten we onze abstracte basisklasse de abstracte API van een bordspel laten definiëren:

openbare abstracte klasse BoardGame {// ... veldverklaringen, constructeurs openbare abstracte leegte play (); // ... concrete methoden}

Vervolgens kunnen we een subklasse maken die de Speel methode:

public class Checkers breidt BoardGame uit {public void play () {// ... implementatie}}

3. Wanneer abstracte klassen gebruiken

Nu, laten we een paar typische scenario's analyseren waarin we de voorkeur zouden moeten geven aan abstracte klassen boven interfaces en concrete klassen:

  • We willen een aantal algemene functionaliteit samenvatten op één plek (hergebruik van code) die meerdere, gerelateerde subklassen zullen delen
  • We moeten een API gedeeltelijk definiëren die onze subklassen gemakkelijk kunnen uitbreiden en verfijnen
  • De subklassen moeten een of meer veelgebruikte methoden of velden erven met beschermde toegangsmodificatoren

Laten we in gedachten houden dat al deze scenario's goede voorbeelden zijn van volledige, op overerving gebaseerde naleving van het Open / Gesloten-principe.

Bovendien, aangezien het gebruik van abstracte klassen impliciet betrekking heeft op basistypen en subtypen, maken we ook gebruik van polymorfisme.

Merk op dat hergebruik van code een zeer dwingende reden is om abstracte klassen te gebruiken, zolang de "is-a" -relatie binnen de klassenhiërarchie behouden blijft.

En Java 8 voegt nog een rimpel toe met standaardmethoden, die soms de plaats kunnen innemen van het maken van een abstracte klasse.

4. Een voorbeeldhiërarchie van bestandslezers

Laten we naar een ander voorbeeld kijken om een ​​beter begrip te krijgen van de functionaliteit die abstracte klassen naar de tafel brengen.

4.1. Een basis abstracte klasse definiëren

Dus als we verschillende soorten bestandslezers willen hebben, kunnen we een abstracte klasse maken die omvat wat gebruikelijk is bij het lezen van bestanden:

openbare abstracte klasse BaseFileReader {beschermd pad filePath; beschermde BaseFileReader (pad filePath) {this.filePath = filePath; } openbaar pad getFilePath () {return filePath; } openbare lijst readFile () gooit IOException {return Files.lines (filePath) .map (this :: mapFileLine) .collect (Collectors.toList ()); } beschermde samenvatting String mapFileLine (String lijn); }

Merk op dat we hebben gemaakt bestandspad beveiligd zodat de subklassen er indien nodig toegang toe hebben. Belangrijker, we hebben iets ongedaan gemaakt: hoe een regel tekst daadwerkelijk te ontleden uit de inhoud van het bestand.

Ons plan is simpel: hoewel onze concrete klassen niet elk een speciale manier hebben om het bestandspad op te slaan of door het bestand te lopen, hebben ze elk een speciale manier om elke regel te transformeren.

Op het eerste gezicht, BaseFileReader lijkt misschien overbodig. Het is echter de basis van een schoon, gemakkelijk uitbreidbaar ontwerp. Van daaruit we kunnen gemakkelijk verschillende versies van een bestandslezer implementeren die zich kunnen concentreren op hun unieke bedrijfslogica.

4.2. Subklassen definiëren

Een natuurlijke implementatie is waarschijnlijk een implementatie die de inhoud van een bestand naar kleine letters converteert:

openbare klasse LowercaseFileReader breidt BaseFileReader uit {openbare LowercaseFileReader (pad filePath) {super (filePath); } @Override public String mapFileLine (String lijn) {return line.toLowerCase (); }}

Of een ander kan er een zijn die de inhoud van een bestand omzet in hoofdletters:

openbare klasse UppercaseFileReader breidt BaseFileReader uit {openbare UppercaseFileReader (pad filePath) {super (filePath); } @Override public String mapFileLine (String lijn) {return line.toUpperCase (); }}

Zoals we kunnen zien aan de hand van dit eenvoudige voorbeeld, elke subklasse kan zich concentreren op zijn unieke gedrag zonder dat u andere aspecten van het lezen van bestanden hoeft te specificeren.

4.3. Met behulp van een subklasse

Ten slotte is het gebruik van een klasse die erft van een abstracte klasse niet anders dan elke andere concrete klasse:

@Test openbare leegte gegevenLowercaseFileReaderInstance_whenCalledreadFile_thenCorrect () gooit uitzondering {URL-locatie = getClass (). GetClassLoader (). GetResource ("files / test.txt") Padpad = Paths.get (location.toURI ()); BaseFileReader lowercaseFileReader = nieuwe LowercaseFileReader (pad); assertThat (lowercaseFileReader.readFile ()). isInstanceOf (List.class); }

Eenvoudigheidshalve bevindt het doelbestand zich onder de src / main / resources / files map. Daarom hebben we een applicatieklasse-lader gebruikt om het pad van het voorbeeldbestand op te halen. Bekijk gerust onze tutorial over class loaders in Java.

5. Conclusie

In dit korte artikel, we leerden de basisprincipes van abstracte klassen in Java, en wanneer we ze moesten gebruiken om abstractie te bereiken en algemene implementatie op één enkele plaats samen te vatten.

Zoals gewoonlijk zijn alle codevoorbeelden die in deze tutorial worden getoond, beschikbaar op GitHub.