"Hej, Amigo! Nu er her et emne, jeg tror, ​​du vil bruge meget. Jeg taler om arv. "

For de uindviede er programmering som magi. Så lad mig starte med en analogi...

Antag, at du er en tryllekunstner, der ønsker at skabe en flyvende hest. Du kunne prøve at fremtrylle en Pegasus. Men da flyvende heste ikke er naturligt forekommende, vil du få det rigtig svært. Du ville have meget arbejde at gøre. Det ville være meget nemmere at starte med en hest og tilkalde nogle vinger.

Arv.  Fordele ved arv - 1

I programmering kalder vi denne proces for «arv» . Antag, at du skal skrive en meget kompleks klasse. Du kan bruge lang tid på at skrive kode fra bunden og derefter udføre lange tests for at finde fejl. Men hvorfor gøre det på den hårde måde? Det er bedre at kigge rundt og se, om den klasse, du leder efter, allerede eksisterer?

Lad os sige, at du finder en klasse, der implementerer 80 % af den funktionalitet, du har brug for. Du kan bare kopiere dens kode til din klasse. Men det ville have flere ulemper:

1) Den klasse, du finder, er muligvis allerede kompileret til bytekode. Du har muligvis ikke adgang til dens kildekode.

2) Du har måske kildekoden til klassen, men arbejder også i en virksomhed, der kan sagsøges for et par milliarder, hvis du bruger selv 6 linjer af en andens kode. Og så vil de sagsøge dig.

3) Dette fører til unødvendig duplikering af masser af kode. Og hvis forfatteren af ​​den anden klasse finder en fejl og retter den, har du stadig fejlen.

Der er en mere elegant løsning, der ikke kræver lovlig tilladelse til den oprindelige klasses kode. I Java kan du blot erklære den anden klasse som forælder til din klasse. Dette svarer til at tilføje den pågældende klasses kode til din egen klasse. Alle data og metoder fra den overordnede klasse vises i din klasse. For eksempel kan du arve fra en «hest», tilføje «vinger» og få en «Pegasus».

Arv.  Fordele ved arv - 2

"Meget interessant. Fortsæt venligst."

"Arv har også andre anvendelser. Antag, at du har ti klasser, der er meget ens. De har matchende data og metoder. Du kan oprette en speciel basisklasse , flytte dataene (og tilknyttede metoder) til basisklassen og have de ti klasser arve fra den. Med andre ord, for hver klasse angiver du, at den har en overordnet klasse, også kendt som en basisklasse."

"Ligesom fordelene ved abstraktion kun virkelig afsløres i forbindelse med indkapsling, forstørres fordelene ved arv af polymorfi. Men det fortæller jeg dig om i morgen. Lad os i dag se på et par eksempler på arv."

"Antag, at vi skriver et skakprogram. Vi skal bruge klasser til skakbrikkerne. Hvilke klasser vil du foreslå, Amigo?"

"Konge, Dronning, Biskop, Ridder, Rook og Bonde."

"Meget godt. Du gik ikke glip af noget."

"Og hvilke data vil du foreslå at gemme i disse klasser?"

"Hver brikkers bordposition (x og y) og værdi. Nogle brikker er trods alt mere værdifulde end andre."

"Og hvad er forskellene mellem disse klasser?"

"De er forskellige i, hvordan de flytter brikkerne. I deres adfærd."

"Ja. Du kan definere dem som klasser som denne:"

class King
{
int x;
int y;
int worth;
void kingMove()
{
//code that defines,
//how the king moves
}
}
class Queen
{
int x;
int y;
int worth;
void queenMove()
{
//code that defines,
//how the queen moves
}
}
class Rook
{
int x;
int y;
int worth;
void rookMove()
{
//code that defines,
//how the rook moves
}
}
class Knight
{
int x;
int y;
int worth;
void knightMove()
{
//code that defines,
//how the knight moves
}
}
class Bishop
{
int x;
int y;
int worth;
void bishopMove()
{
//code that defines,
//how the bishop moves
}
}
class Pawn
{
int x;
int y;
int worth;
void pawnMove()
{
//code that defines,
//how the pawn moves
}
}

"Ja, det er præcis sådan, jeg ville skrive det."

"Men se på, hvordan du kunne bruge arv til at skrive mindre kode. Vi kan flytte identiske metoder og data ind i en fælles klasse. Lad os kalde det ChessItem. Det giver ikke mening at lave ChessItem-objekter, da de ikke svarer til evt. skakbrik. Men klassen ville være yderst hjælpsom:"

class King extends ChessItem
{
void kingMove()
{
//code that defines,
//how the king moves
}
}
class Queen extends ChessItem
{
void queenMove()
{
//code that defines,
//how the queen moves
}
}
class Rook extends ChessItem
{
void rookMove()
{
//code that defines,
//how the rook moves
}
}
 class ChessItem
{
int x;
int y;
int worth;
}
 
class Knight extends ChessItem
{
void knightMove()
{
//code that defines,
//how the knight moves
}
}
class Bishop extends ChessItem
{
void bishopMove()
{
//code that defines,
//how the bishop moves
}
}
class Pawn extends ChessItem
{
void pawnMove()
{
//code that defines,
//how the pawn moves
}
}

"Hvor interessant!"

"Absolut! Fordelen er især stor i projekter, der indeholder tusindvis af forskellige objekter og hundredvis af klasser. I dette tilfælde kan korrekt valgte klasser ikke kun forenkle logikken markant, men også reducere den nødvendige kode med en faktor på ti."

"Så hvad skal du gøre for at arve en klasse?"

"Efter at have erklæret en klasse, bruger vi nøgleordet ' forlænger ', efterfulgt af navnet på den overordnede klasse. Du kan kun arve fra én klasse. "

Arv.  Fordele ved arv - 3

Billedet viser en "ko", der har arvet fra en «gris». «Grisen» arver fra «kyllingen», og «kyllingen» arver fra «ægget». Hver klasse har kun én forælder! En sådan arv er ikke altid logisk. Hvis du kun har en gris, men du virkelig har brug for en ko, kan programmører ofte ikke modstå ønsket om at lave en «ko» ud af «grisen».

"Men hvad nu hvis jeg vil arve fra to klasser? Er der noget jeg kan gøre?!"

"Ikke rigtig. Java-klasser understøtter ikke multipel nedarvning af implementering: en klasse kan kun have en enkelt overordnet klasse. Men der er multipel nedarvning af typen, hvilket betyder, at en klasse kan implementere mere end én grænseflade. Dette afhjælper problemet lidt. "

"Jeg kan se. Og hvad er en grænseflade?"

"Jeg fortæller dig om grænseflader i morgen. Lad os nu fortsætte med at dykke ned i arv."