Abstrakte klasser i Java

1. Oversikt

Det er mange tilfeller når vi implementerer en kontrakt der vi ønsker å utsette noen deler av implementeringen for å bli fullført senere. Vi kan enkelt oppnå dette i Java gjennom abstrakte klasser.

I denne opplæringen lærer vi det grunnleggende om abstrakte klasser i Java, og i hvilke tilfeller de kan være nyttige.

2. Nøkkelbegreper for abstrakte klasser

Før du dykker inn når du skal bruke en abstrakt klasse, la oss se på deres mest relevante egenskaper:

  • Vi definerer en abstrakt klasse med abstrakt modifiserende forut for klasse nøkkelord
  • En abstrakt klasse kan underklasseres, men den kan ikke instantieres
  • Hvis en klasse definerer en eller flere abstrakt metoder, så må klassen selv erklæres abstrakt
  • En abstrakt klasse kan erklære både abstrakte og konkrete metoder
  • En underklasse avledet fra en abstrakt klasse må enten implementere alle basisklassens abstrakte metoder eller være abstrakt i seg selv

For å bedre forstå disse konseptene, lager vi et enkelt eksempel.

La oss få vår grunnleggende abstrakte klasse til å definere abstrakt API for et brettspill:

offentlig abstrakt klasse BoardGame {// ... feltdeklarasjoner, konstruktører offentlig abstrakt ugyldig spill (); // ... konkrete metoder}

Deretter kan vi lage en underklasse som implementerer spille metode:

public class Checkers utvider BoardGame {public void play () {// ... implementering}}

3. Når skal du bruke abstrakte klasser?

Nå, la oss analysere noen typiske scenarier der vi foretrekker abstrakte klasser fremfor grensesnitt og konkrete klasser:

  • Vi ønsker å kapsle inn noen vanlige funksjoner på ett sted (kode gjenbruk) som flere, relaterte underklasser vil dele
  • Vi må delvis definere en API som underklassene våre enkelt kan utvide og avgrense
  • Underklassene må arve en eller flere vanlige metoder eller felt med beskyttede tilgangsmodifikatorer

La oss huske på at alle disse scenariene er gode eksempler på full, arvbasert overholdelse av Open / Closed-prinsippet.

Dessuten, siden bruk av abstrakte klasser implisitt omhandler basetyper og undertyper, utnytter vi også polymorfisme.

Merk at gjenbruk av kode er en veldig overbevisende grunn til å bruke abstrakte klasser, så lenge forholdet "er-en" i klassehierarkiet er bevart.

Og Java 8 legger til et nytt rynke med standardmetoder, som noen ganger kan ta stedet å trenge å lage en abstrakt klasse helt.

4. Et eksempel Hierarki av fillesere

For å forstå klarere funksjonaliteten som abstrakte klasser bringer til bordet, la oss se på et annet eksempel.

4.1. Definere en base abstrakt klasse

Så hvis vi ønsket å ha flere typer fillesere, kan vi lage en abstrakt klasse som innkapsler det som er vanlig med fillesing:

offentlig abstrakt klasse BaseFileReader {beskyttet sti filePath; beskyttet BaseFileReader (Path filePath) {this.filePath = filePath; } offentlig sti getFilePath () {return filePath; } offentlig liste readFile () kaster IOException {return Files.lines (filePath) .map (dette :: mapFileLine) .collect (Collectors.toList ()); } beskyttet abstrakt String mapFileLine (strengelinje); }

Merk at vi har laget filePath beskyttet slik at underklassene får tilgang til det om nødvendig. Enda viktigere, vi har igjen noe ugjort: hvordan man faktisk kan analysere en tekstlinje fra filens innhold.

Planen vår er enkel: Selv om våre konkrete klasser ikke hver har en spesiell måte å lagre filstien på eller gå gjennom filen, vil de hver ha en spesiell måte å transformere hver linje.

Ved første blikk, BaseFileReader kan virke unødvendig. Det er imidlertid grunnlaget for et rent, lett utvidbart design. Fra det, vi kan enkelt implementere forskjellige versjoner av en filleser som kan fokusere på deres unike forretningslogikk.

4.2. Definere underklasser

En naturlig implementering er sannsynligvis en som konverterer filens innhold til små bokstaver:

public class LowercaseFileReader utvider BaseFileReader {public LowercaseFileReader (Path filePath) {super (filePath); } @ Override public String mapFileLine (String line) {retur line.toLowerCase (); }}

Eller en annen kan konvertere en fils innhold til store bokstaver:

public class UppercaseFileReader utvider BaseFileReader {public UppercaseFileReader (Path filePath) {super (filePath); } @ Override public String mapFileLine (String line) {retur line.toUpperCase (); }}

Som vi kan se fra dette enkle eksemplet, hver underklasse kan fokusere på sin unike oppførsel uten å måtte spesifisere andre aspekter ved fillesing.

4.3. Bruke en underklasse

Til slutt, å bruke en klasse som arver fra en abstrakt, er ikke annerledes enn noen annen konkret klasse:

@Test offentlig ugyldig givenLowercaseFileReaderInstance_whenCalledreadFile_thenCorrect () kaster unntak {URL location = getClass (). GetClassLoader (). GetResource ("files / test.txt") Path path = Paths.get (location.toURI ()); BaseFileReader små bokstaverFileReader = nye små bokstaverFileReader (sti); assertThat (smallcaseFileReader.readFile ()). isInstanceOf (List.class); }

For enkelhets skyld ligger målfilen under src / main / resources / files mappe. Derfor brukte vi en programklasselaster for å få banen til eksempelfilen. Ta gjerne en titt på veiledningen vår om klasselaster i Java.

5. Konklusjon

I denne raske artikkelen, vi lærte det grunnleggende om abstrakte klasser i Java, og når vi skulle bruke dem for å oppnå abstraksjon og innkapsling av felles implementering på ett sted.

Som vanlig er alle kodeeksemplene som vises i denne opplæringen tilgjengelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found