Arv (Inheritance)
Arv låter en subklass (barn) ärva fält och metoder från en superklass (förälder). Detta skapar en är-en-relation och främjar återanvändning.
// Superklass (förälder)
public class Fordon {
protected String registeringsnummer;
public Fordon(String registeringsnummer) {
this.registeringsnummer = registeringsnummer;
}
public void starta() {
System.out.println("Fordonet startar");
}
public void stanna() {
System.out.println("Fordonet stannar");
}
}
// Subklass (barn) — ärver från Fordon
public class Bil extends Fordon {
private int antalDorrar;
public Bil(String regNr, int antalDorrar) {
super(regNr); // anropar förälderns konstruktor
this.antalDorrar = antalDorrar;
}
public void tuta() {
System.out.println("Tut tut!");
}
}
// Användning
Bil b = new Bil("ABC123", 4);
b.starta(); // ärvd från Fordon
b.tuta(); // egen metod
b.stanna(); // ärvd från Fordon
Nyckelordet super används för att referera till föräldraklassen — både för konstruktorer och överskuggade metoder.
Arvshierarkier
En subklass kan också bli superklass för andra klasser:
public class Fordon { /* ... */ }
public class Bil extends Fordon { /* ... */ }
public class Elbil extends Bil { /* ... */ }
public class Sportbil extends Bil { /* ... */ }
De flesta språk (Java, C#, PHP) tillåter enkelarv — en klass kan bara ha en direkt förälder. C++ och Python tillåter flerarv (flera föräldrar), vilket är kraftfullt men kan leda till Diamond-problemet.
Överskuggning av metoder (Overriding)
En subklass kan ge en egen implementation av en metod från superklassen:
public class Fordon {
public void beskrivning() {
System.out.println("Ett fordon");
}
}
public class Bil extends Fordon {
@Override
public void beskrivning() {
System.out.println("En bil med " + antalDorrar + " dörrar");
}
}
Fordon f = new Bil();
f.beskrivning(); // "En bil med 4 dörrar" — polymorfism i praktiken
@Override-annotationen är valfri men rekommenderas — kompilatorn varnar då om du stavat fel eller signaturen inte matchar.
När ska man använda arv?
Arv passar bra när:
- Det finns en tydlig är-en-relation (Förälder-Barn)
- Subklassen behöver det mesta av superklassens beteende
- Du vill utnyttja polymorfism
public class Anstalld { /* ... */ }
public class Chef extends Anstalld { /* ... */ } // Chef ÄR-EN Anställd
public class Utvecklare extends Anstalld { /* ... */ } // Utvecklare ÄR-EN Anställd
Arv passar dåligt när:
- Relationen är mer har-en än är-en
- Du bara vill återanvända en liten del av superklassen
- Subklassen behöver override:a många metoder
Komposition (Composition)
Komposition innebär att en klass innehåller andra objekt som fält — en har-en-relation. Detta är ofta flexiblare än arv.
// KOMPOSITION — föredra detta framför arv när det är möjligt
public class Motor {
public void starta() {
System.out.println("Motorn startar");
}
}
public class Hjul {
public void rulla() {
System.out.println("Hjulen rullar");
}
}
public class Bil {
private Motor motor = new Motor(); // Bil HAR-EN motor
private Hjul[] hjul = new Hjul[4]; // Bil HAR-FYRA hjul
public Bil() {
for (int i = 0; i < 4; i++) {
hjul[i] = new Hjul();
}
}
public void starta() {
motor.starta();
for (Hjul h : hjul) h.rulla();
}
}
Fördelar med komposition framför arv
| Aspekt | Arv | Komposition |
|---|---|---|
| Koppling | Stark (tight coupling) | Lös (loose coupling) |
| Flexibilitet | Låst vid kompilering | Kan ändras vid runtime |
| Återanvändning | Ärver allt (även oönskat) | Väljer bara vad som behövs |
| Testbarhet | Svårare (beroende av förälder) | Lätt (beroenden kan mockas) |
| Ändringar | Ändring i superklass påverkar alla | Isolerad |
Favor Composition over Inheritance
Detta är en välkändprincip inom OOP. Exempel där komposition vinner:
// DÅLIGT med arv — en klass per kombination
public class Anstalld { /* ... */ }
public class ChefMedBonus extends Anstalld { /* ... */ }
public class ChefUtanBonus extends Anstalld { /* ... */ }
public class KonsultMedBonus extends Anstalld { /* ... */ }
public class KonsultUtanBonus extends Anstalld { /* ... */ }
// Exploderar snabbt!
// BRA med komposition
public interface BonusPolicy { double beraknaBonus(double lon); }
public class Anstalld {
private BonusPolicy bonusPolicy; // injiceras
public double getTotalErsattning() {
return lon + bonusPolicy.beraknaBonus(lon);
}
}
Många designmönster (Strategy, Decorator, Bridge) bygger på komposition istället för arv.
protected — en medelväg
protected låter subklasser komma åt förälderns fält och metoder, men håller dem dolda för omvärlden:
public class Fordon {
protected String registeringsnummer; // åtkomligt i subklasser
private double serviceKostnad; // bara i Fordon
}
Var dock försiktig med protected — det skapar ändå en stark koppling mellan förälder och barn.
final / sealed — förhindra arv
Ibland vill du förhindra att en klass ärvs:
// Java — final
public final class String { /* ... */ }
// C# — sealed
public sealed class String { /* ... */ }
// PHP — final
final class Database { /* ... */ }
Varför? För att garantera oföränderlighet (immutability), säkerhet eller att kontraktet inte kan brytas.
Fortsätt till Polymorfism.
Av Victor Hernandez från Bytebase.se