1. Syntaxe d’une classe abstraite
Voyons ce qu’est une classe abstraite en Java et à quoi elle sert. Imaginez que vous avez une boîte de LEGO : vous avez un ensemble de pièces (par exemple, « roues », « briques »), et des pièces spécifiques pour des modèles particuliers. Une classe abstraite, c’est comme une notice générale expliquant comment manipuler les pièces sans décrire l’assemblage d’un modèle précis : les briques s’emboîtent, les roues se fixent aux axes, on peut empiler les pièces par couches. La classe abstraite décrit ces règles communes et les étapes obligatoires, sans détailler la construction d’un modèle concret. Les modèles concrets sont les sous-classes : elles prennent l’instruction générale et ajoutent les étapes manquantes.
En Java, une classe abstraite est une classe qui ne peut pas être instanciée directement (on ne peut pas faire new AbstractClass()), mais dont on peut hériter et implémenter ses méthodes « inachevées ».
Les classes abstraites sont utiles lorsque :
- Un groupe d’objets partage un comportement ou un état commun, mais une partie de la logique diffère.
- Vous souhaitez implémenter une partie de la fonctionnalité « par défaut » et laisser le reste aux sous-classes.
- Vous souhaitez interdire la création d’objets de cette classe directement (par exemple, « Animal » n’existe pas tout seul, mais « Chat » — oui).
Comment déclarer une classe abstraite
C’est tout simple : on met le mot-clé abstract avant le mot class.
public abstract class Animal {
// Champs (par exemple, le nom de l'animal)
protected String name;
// Constructeur
public Animal(String name) {
this.name = name;
}
// Méthode abstraite — déclaration uniquement, sans implémentation !
public abstract void makeSound();
// Méthode normale (implémentée)
public void sleep() {
System.out.println(name + " dort : Zzz...");
}
}
Particularités :
- Une classe abstraite peut contenir à la fois des méthodes implémentées et des méthodes abstraites.
- Une classe abstraite peut contenir des champs, des constructeurs et même des méthodes static.
- On ne peut pas créer un objet d’une classe abstraite directement :
-
Animal a = new Animal("Quelqu'un"); // Erreur !
Comment déclarer une méthode abstraite
Une méthode abstraite est une méthode sans corps, c’est-à-dire sans accolades ni code à l’intérieur. Elle est déclarée avec le mot-clé abstract et se termine obligatoirement par un point-virgule ;.
public abstract void makeSound();
- Les méthodes abstraites ne peuvent être déclarées qu’à l’intérieur d’une classe abstraite.
- Une classe qui hérite d’une classe abstraite doit implémenter toutes les méthodes abstraites, sinon elle devient elle aussi abstract.
2. Héritage des classes abstraites : comment ça fonctionne
Voyons un exemple concret. Supposons que nous avons une classe abstraite Animal avec une méthode abstraite makeSound(). Créons maintenant une sous-classe Dog qui implémente cette méthode :
public class Dog extends Animal {
public Dog(String name) {
super(name); // Appel du constructeur de la classe de base
}
@Override
public void makeSound() {
System.out.println(name + " aboie : Ouaf-Ouaf !");
}
}
Et encore une classe dérivée :
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " miaule : Miaou !");
}
}
Nous pouvons maintenant utiliser ces classes dans le programme :
public class Main {
public static void main(String[] args) {
Animal dog = new Dog("Sharik");
Animal cat = new Cat("Murka");
dog.makeSound(); // Sharik aboie : Ouaf-Ouaf !
cat.makeSound(); // Murka miaule : Miaou !
dog.sleep(); // Sharik dort : Zzz...
cat.sleep(); // Murka dort : Zzz...
}
}
À noter :
Nous pouvons déclarer des variables de type Animal, mais créer des objets uniquement des sous-classes concrètes (non abstraites).
Schéma : à quoi cela ressemble
. Animal (abstract)
/ \
Dog Cat
(implémente makeSound) (implémente makeSound)
3. Quand utiliser une classe abstraite et quand une interface ?
C’est l’une des questions les plus courantes en entretien, et pour cause ! Voyons cela :
- Classe abstraite — lorsque les objets ont un état commun (par exemple, des champs), une logique commune (des méthodes avec implémentation) et que vous voulez fournir un « squelette » de comportement avec possibilité d’extension.
- Interface — lorsque vous souhaitez définir uniquement un ensemble de méthodes (un contrat), sans implémentation ni état. Depuis Java 8, les interfaces ont des méthodes default/static, mais l’interface reste axée sur « ce que l’objet doit savoir faire », pas « comment il le fait ».
Exemple de la vie réelle :
« Oiseau » — classe abstraite : tous les oiseaux ont un bec, des ailes, et peuvent voler (mais pas de la même façon).
« Volant » — interface : non seulement les oiseaux savent voler, mais aussi les avions et les super-héros ! Ils volent tous différemment, mais l’essentiel est qu’ils savent le faire.
4. Exemples pratiques
Exemple 1 : Classe abstraite avec implémentation partielle
Supposons que vous créez un jeu avec différents types de transports. Ils peuvent tous se déplacer, mais de manières différentes. Tous ont toutefois une vitesse, un nom et une manière standard de s’arrêter.
public abstract class Transport {
protected String name;
protected int speed;
public Transport(String name, int speed) {
this.name = name;
this.speed = speed;
}
// Méthode abstraite — implémentation dans les sous-classes
public abstract void move();
// Méthode implémentée
public void stop() {
System.out.println(name + " s'est arrêté.");
}
}
public class Car extends Transport {
public Car(String name, int speed) {
super(name, speed);
}
@Override
public void move() {
System.out.println(name + " roule sur la route à la vitesse de " + speed + " km/h.");
}
}
public class Bicycle extends Transport {
public Bicycle(String name, int speed) {
super(name, speed);
}
@Override
public void move() {
System.out.println(name + " pédale à la vitesse de " + speed + " km/h.");
}
}
Utilisation :
Transport car = new Car("Toyota", 120);
Transport bike = new Bicycle("Stels", 25);
car.move(); // Toyota roule sur la route à la vitesse de 120 km/h.
bike.move(); // Stels pédale à la vitesse de 25 km/h.
car.stop(); // Toyota s'est arrêté.
bike.stop(); // Stels s'est arrêté.
Exemple 2: Méthode abstraite avec paramètre
public abstract class Shape {
public abstract double area();
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public class Rectangle extends Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
Utilisation :
Shape c = new Circle(3);
Shape r = new Rectangle(4, 5);
System.out.println("Aire du cercle : " + c.area());
System.out.println("Aire du rectangle : " + r.area());
5. Particularités et subtilités des classes abstraites
- Une classe abstraite peut utiliser n’importe quel modificateur d’accès : public, protected, private (par exemple, pour les champs).
- On peut déclarer une classe abstraite sans méthodes abstraites. C’est utile si vous voulez simplement interdire l’instanciation de la classe de base.
- Si une classe hérite d’une classe abstraite mais n’implémente pas toutes les méthodes abstraites, elle doit elle aussi être déclarée abstract.
- Une classe abstraite peut avoir des constructeurs. Ils sont appelés lors de la création d’un objet d’une sous-classe (via super(...)).
- Les méthodes abstraites ne peuvent pas être private (sinon elles ne pourraient pas être implémentées dans la sous-classe).
- Les classes abstraites peuvent contenir des méthodes et des champs static.
- Une classe abstraite peut implémenter des interfaces, sans être obligée d’en implémenter les méthodes — les sous-classes peuvent le faire.
Tableau : comparaison entre classe abstraite et interface
| Classe abstraite | Interface | |
|---|---|---|
| Mot-clé | |
|
| Peut contenir des champs | Oui (n’importe lesquels) | Depuis Java 8 — seulement static/final |
| Méthodes avec implémentation | Oui | Depuis Java 8 — default/static |
| Méthodes abstraites | Oui | Oui |
| Héritage multiple | Non | Oui (on peut implémenter plusieurs interfaces) |
| Constructeurs | Oui | Non |
| Peut-on créer un objet | Non | Non |
6. Erreurs typiques avec les classes et méthodes abstraites
Erreur n°1 : tentative de créer une instance d’une classe abstraite.
Si vous écrivez new Animal("Quelqu'un"), le compilateur vous rappellera immédiatement que les classes abstraites ne sont pas faites pour cela. Rappelez-vous : l’abstraction est un « squelette », pas un « organisme vivant ».
Erreur n°2 : vous avez oublié d’implémenter toutes les méthodes abstraites dans la sous-classe.
Si votre classe n’implémente ne serait-ce qu’une seule méthode abstraite de la classe de base, elle doit elle-même être déclarée abstract, sinon vous obtiendrez une erreur de compilation.
Erreur n°3 : méthode abstraite déclarée en dehors d’une classe abstraite.
En Java, on ne peut pas déclarer une méthode abstraite dans une classe ordinaire (non abstraite) — le compilateur protestera immédiatement.
Erreur n°4 : tentative de rendre une méthode abstraite private ou static.
Les méthodes abstraites ne peuvent pas être private (sinon elles ne peuvent pas être redéfinies) et ne peuvent pas être static (car les méthodes statiques ne sont pas redéfinies). Elles ne peuvent pas non plus être final, puisque final interdit la redéfinition.
Erreur n°5 : oublier le modificateur d’accès d’une méthode abstraite.
Si vous n’indiquez pas explicitement le modificateur, la méthode aura une visibilité de paquet (package-private), ce qui n’est parfois pas ce que vous vouliez.
GO TO FULL VERSION