Java Language
Héritage
Recherche…
Introduction
L'héritage est une caractéristique orientée objet de base dans laquelle une classe acquiert et s'étend sur les propriétés d'une autre classe, à l'aide du mot extends
clé extends
. Pour les interfaces et les implements
mots-clés, voir interfaces .
Syntaxe
- classe ClassB étend ClassA {...}
- class ClassB implémente InterfaceA {...}
- interface InterfaceB étend InterfaceA {...}
- classe ClassB étend ClassA implémente InterfaceC, InterfaceD {...}
- classe abstraite AbstractClassB étend ClassA {...}
- classe abstraite AbstractClassB étend AbstractClassA {...}
- Classe abstraite AbstractClassB extend ClassA implémente InterfaceC, InterfaceD {...}
Remarques
L'héritage est souvent combiné avec des génériques afin que la classe de base ait un ou plusieurs paramètres de type. Voir Création d'une classe générique .
Classes abstraites
Une classe abstraite est une classe marquée avec le mot-clé abstract
. Contrairement aux classes non abstraites, il peut contenir des méthodes abstraites sans implémentation. Il est cependant valable de créer une classe abstraite sans méthodes abstraites.
Une classe abstraite ne peut pas être instanciée. Il peut être sous-classé (étendu) tant que la sous-classe est soit abstraite, soit implémenter toutes les méthodes marquées comme abstraites par les super-classes.
Un exemple de classe abstraite:
public abstract class Component {
private int x, y;
public setPosition(int x, int y) {
this.x = x;
this.y = y;
}
public abstract void render();
}
La classe doit être marquée abstraite lorsqu'elle a au moins une méthode abstraite. Une méthode abstraite est une méthode sans implémentation. D'autres méthodes peuvent être déclarées dans une classe abstraite ayant une implémentation afin de fournir un code commun à toutes les sous-classes.
Tenter d’instancier cette classe fournira une erreur de compilation:
//error: Component is abstract; cannot be instantiated
Component myComponent = new Component();
Cependant, une classe qui étend Component
et fournit une implémentation pour toutes ses méthodes abstraites et peut être instanciée.
public class Button extends Component {
@Override
public void render() {
//render a button
}
}
public class TextBox extends Component {
@Override
public void render() {
//render a textbox
}
}
Les instances d'héritage de classes peuvent également être converties en tant que classe parente (héritage normal) et elles fournissent un effet polymorphe lorsque la méthode abstraite est appelée.
Component myButton = new Button();
Component myTextBox = new TextBox();
myButton.render(); //renders a button
myTextBox.render(); //renders a text box
Classes abstraites vs interfaces
Les classes abstraites et les interfaces fournissent toutes deux un moyen de définir des signatures de méthode tout en exigeant que la classe d'extension / d'implémentation fournisse l'implémentation.
Il existe deux différences clés entre les classes abstraites et les interfaces:
- Une classe ne peut étendre qu'une seule classe, mais peut implémenter de nombreuses interfaces.
- Une classe abstraite peut contenir
static
champs d'instance (nonstatic
), mais les interfaces ne peuvent contenir questatic
champsstatic
.
Les méthodes déclarées dans les interfaces ne pouvant contenir des implémentations, des classes abstraites ont été utilisées lorsqu'il était utile de fournir des méthodes supplémentaires que les implémentations appelaient les méthodes abstraites.
Java 8 permet aux interfaces de contenir des méthodes par défaut , généralement implémentées à l'aide des autres méthodes de l'interface , ce qui rend les interfaces et les classes abstraites aussi puissantes à cet égard.
Sous-classes anonymes de classes abstraites
Comme commodité, Java permet l'instanciation d'instances anonymes de sous-classes de classes abstraites, qui fournissent des implémentations pour les méthodes abstraites lors de la création du nouvel objet. En utilisant l'exemple ci-dessus, cela pourrait ressembler à ceci:
Component myAnonymousComponent = new Component() {
@Override
public void render() {
// render a quick 1-time use component
}
}
Héritage statique
La méthode statique peut être héritée de manière similaire aux méthodes normales, mais contrairement aux méthodes normales, il est impossible de créer des méthodes " abstraites " pour forcer la substitution de la méthode statique. Ecrire une méthode avec la même signature qu'une méthode statique dans une super classe semble être une forme de substitution, mais cela crée une nouvelle fonction qui cache l'autre.
public class BaseClass {
public static int num = 5;
public static void sayHello() {
System.out.println("Hello");
}
public static void main(String[] args) {
BaseClass.sayHello();
System.out.println("BaseClass's num: " + BaseClass.num);
SubClass.sayHello();
//This will be different than the above statement's output, since it runs
//A different method
SubClass.sayHello(true);
StaticOverride.sayHello();
System.out.println("StaticOverride's num: " + StaticOverride.num);
}
}
public class SubClass extends BaseClass {
//Inherits the sayHello function, but does not override it
public static void sayHello(boolean test) {
System.out.println("Hey");
}
}
public static class StaticOverride extends BaseClass {
//Hides the num field from BaseClass
//You can even change the type, since this doesn't affect the signature
public static String num = "test";
//Cannot use @Override annotation, since this is static
//This overrides the sayHello method from BaseClass
public static void sayHello() {
System.out.println("Static says Hi");
}
}
L'exécution de l'une de ces classes produit la sortie:
Hello
BaseClass's num: 5
Hello
Hey
Static says Hi
StaticOverride's num: test
Notez que contrairement à l'héritage normal, les méthodes d'héritage statiques ne sont pas masquées. Vous pouvez toujours appeler la méthode sayHello
base en utilisant BaseClass.sayHello()
. Mais les classes héritent des méthodes statiques si aucune méthode avec la même signature n'est trouvée dans la sous-classe. Si les signatures de deux méthodes varient, les deux méthodes peuvent être exécutées à partir de la sous-classe, même si le nom est identique.
Les champs statiques se cachent de la même manière.
Utiliser 'final' pour restreindre l'héritage et remplacer
Classes finales
Lorsqu'il est utilisé dans une déclaration de class
, le modificateur final
empêche les autres classes d'être déclarées qui extend
la classe. Une classe final
est une classe "feuille" dans la hiérarchie des classes d'héritage.
// This declares a final class
final class MyFinalClass {
/* some code */
}
// Compilation error: cannot inherit from final MyFinalClass
class MySubClass extends MyFinalClass {
/* more code */
}
Cas d'utilisation pour les classes finales
Les classes finales peuvent être combinées à un constructeur private
pour contrôler ou empêcher l'instanciation d'une classe. Cela peut être utilisé pour créer une soi-disant "classe utilitaire" qui définit uniquement les membres statiques; c'est-à-dire des constantes et des méthodes statiques.
public final class UtilityClass {
// Private constructor to replace the default visible constructor
private UtilityClass() {}
// Static members can still be used as usual
public static int doSomethingCool() {
return 123;
}
}
Les classes immuables doivent également être déclarées comme final
. (Une classe immuable est une classe dont les instances ne peuvent pas être modifiées après leur création; reportez-vous à la rubrique Objets indisponibles .) En faisant cela, vous ne pouvez pas créer une sous-classe mutable d'une classe immuable. Cela violerait le principe de substitution de Liskov qui exige qu'un sous-type obéisse au «contrat comportemental» de ses supertypes.
D'un point de vue pratique, déclarer une classe immuable comme étant final
permet de raisonner plus facilement sur le comportement du programme. Il résout également les problèmes de sécurité dans le scénario où du code non fiable est exécuté dans un sandbox de sécurité. (Par exemple, puisque String
est déclaré comme final
, une classe de confiance n'a pas à craindre d'être amenée à accepter une sous-classe mutable, que l'appelant non fiable pourrait alors modifier subrepticement.)
Un inconvénient des classes final
est qu’elles ne fonctionnent pas avec certains cadres moqueurs tels que Mockito. Mise à jour: Mockito version 2 supporte maintenant les moqueries des classes finales.
Méthodes finales
Le final
modificateur peut également être appliqué aux méthodes pour éviter qu'elles soient remplacées dans les sous-classes:
public class MyClassWithFinalMethod {
public final void someMethod() {
}
}
public class MySubClass extends MyClassWithFinalMethod {
@Override
public void someMethod() { // Compiler error (overridden method is final)
}
}
Les méthodes finales sont généralement utilisées lorsque vous souhaitez restreindre ce qu'une sous-classe peut changer dans une classe sans interdire complètement les sous-classes.
Le final
modificateur peut également être appliqué aux variables, mais la signification de final
pour les variables n’est pas liée à l’héritage.
Le principe de substitution de Liskov
Substituabilité est un principe dans la programmation orientée objet présenté par Barbara Liskov dans une keynote de la conférence 1987 indiquant que, si la classe B
est une sous - classe de la classe A
, alors où A
est prévu, B
peut être utilisé à la place:
class A {...}
class B extends A {...}
public void method(A obj) {...}
A a = new B(); // Assignment OK
method(new B()); // Passing as parameter OK
Cela s'applique également lorsque le type est une interface, où il n'y a pas besoin de relation hiérarchique entre les objets:
interface Foo {
void bar();
}
class A implements Foo {
void bar() {...}
}
class B implements Foo {
void bar() {...}
}
List<Foo> foos = new ArrayList<>();
foos.add(new A()); // OK
foos.add(new B()); // OK
Maintenant, la liste contient des objets qui ne proviennent pas de la même hiérarchie de classes.
Héritage
Avec l'utilisation du mot extends
clé extend parmi les classes, toutes les propriétés de la super-classe (également appelée classe parent ou classe de base ) sont présentes dans la sous-classe (également appelée classe enfant ou classe dérivée ).
public class BaseClass {
public void baseMethod(){
System.out.println("Doing base class stuff");
}
}
public class SubClass extends BaseClass {
}
Les instances de SubClass
ont hérité de la méthode baseMethod()
:
SubClass s = new SubClass();
s.baseMethod(); //Valid, prints "Doing base class stuff"
Un contenu supplémentaire peut être ajouté à une sous-classe. Cela permet des fonctionnalités supplémentaires dans la sous-classe sans aucune modification de la classe de base ou de toute autre sous-classe de cette même classe de base:
public class Subclass2 extends BaseClass {
public void anotherMethod() {
System.out.println("Doing subclass2 stuff");
}
}
Subclass2 s2 = new Subclass2();
s2.baseMethod(); //Still valid , prints "Doing base class stuff"
s2.anotherMethod(); //Also valid, prints "Doing subclass2 stuff"
Les champs sont également hérités:
public class BaseClassWithField {
public int x;
}
public class SubClassWithField extends BaseClassWithField {
public SubClassWithField(int x) {
this.x = x; //Can access fields
}
}
private
champs et méthodes private
existent toujours dans la sous-classe, mais ne sont pas accessibles:
public class BaseClassWithPrivateField {
private int x = 5;
public int getX() {
return x;
}
}
public class SubClassInheritsPrivateField extends BaseClassWithPrivateField {
public void printX() {
System.out.println(x); //Illegal, can't access private field x
System.out.println(getX()); //Legal, prints 5
}
}
SubClassInheritsPrivateField s = new SubClassInheritsPrivateField();
int x = s.getX(); //x will have a value of 5.
En Java, chaque classe peut s'étendre au maximum sur une autre classe.
public class A{}
public class B{}
public class ExtendsTwoClasses extends A, B {} //Illegal
Ceci est connu sous le nom d'héritage multiple, et bien qu'il soit légal dans certaines langues, Java ne le permet pas avec les classes.
En conséquence, chaque classe possède une chaîne ancestrale non ramifiée de classes menant à Object
, à partir de laquelle toutes les classes descendent.
Méthodes d'héritage et de statique
En Java, les classes parent et enfant peuvent avoir des méthodes statiques avec le même nom. Mais dans de tels cas, l'implémentation de la méthode statique dans child cache l'implémentation de la classe parente, ce n'est pas une substitution de méthode. Par exemple:
class StaticMethodTest {
// static method and inheritance
public static void main(String[] args) {
Parent p = new Child();
p.staticMethod(); // prints Inside Parent
((Child) p).staticMethod(); // prints Inside Child
}
static class Parent {
public static void staticMethod() {
System.out.println("Inside Parent");
}
}
static class Child extends Parent {
public static void staticMethod() {
System.out.println("Inside Child");
}
}
}
Les méthodes statiques sont liées à une classe et non à une instance et cette liaison de méthode se produit au moment de la compilation. Etant donné que dans le premier appel à staticMethod()
, référence de classe parent p
a été utilisé, Parent
la version de de staticMethod()
est invoquée. En second cas, nous avons jetés p
dans l' Child
classe, Child
de staticMethod()
exécuté.
Ombrage variable
Les variables sont SHADOWED et les méthodes sont OVERRIDDEN. La variable à utiliser dépend de la classe dont la variable est déclarée. La méthode à utiliser dépend de la classe réelle de l'objet référencé par la variable.
class Car {
public int gearRatio = 8;
public String accelerate() {
return "Accelerate : Car";
}
}
class SportsCar extends Car {
public int gearRatio = 9;
public String accelerate() {
return "Accelerate : SportsCar";
}
public void test() {
}
public static void main(String[] args) {
Car car = new SportsCar();
System.out.println(car.gearRatio + " " + car.accelerate());
// will print out 8 Accelerate : SportsCar
}
}
Rétrécissement et élargissement des références d'objet
Lancer une instance d'une classe de base dans une sous-classe comme dans: b = (B) a;
est appelé rétrécissement (car vous essayez de restreindre l'objet de classe de base à un objet de classe plus spécifique) et nécessite un transtypage explicite.
Convertir une instance d'une sous-classe en une classe de base comme dans: A a = b;
est appelé l' élargissement et n'a pas besoin d'une conversion de type.
Pour illustrer, considérons les déclarations de classe suivantes et le code de test:
class Vehicle {
}
class Car extends Vehicle {
}
class Truck extends Vehicle {
}
class MotorCycle extends Vehicle {
}
class Test {
public static void main(String[] args) {
Vehicle vehicle = new Car();
Car car = new Car();
vehicle = car; // is valid, no cast needed
Car c = vehicle // not valid
Car c = (Car) vehicle; //valid
}
}
La déclaration Vehicle vehicle = new Car();
est une instruction Java valide. Chaque instance de Car
est également un Vehicle
. Par conséquent, l'affectation est légale sans nécessiter un transtypage explicite.
Par contre, Car c = vehicle;
n'est pas valide. Le type statique de la variable vehicle
est Vehicle
ce qui signifie qu’il peut se référer à une instance de Car
, Camion ,
MotorCycle , or any other current or future subclass of
Véhicule . (Or indeed, an instance of
Vehicle itself, since we did not declare it as an
class.) The assignment cannot be allowed, since that might lead to
abstraite class.) The assignment cannot be allowed, since that might lead to
voiture à se referring to a
instance de Truck`.
Pour éviter cette situation, nous devons ajouter un type-cast explicite:
Car c = (Car) vehicle;
Le type-cast indique au compilateur que nous nous attendons à la valeur du vehicle
soit une Car
ou une sous - classe de Car
. Si nécessaire, le compilateur insérera du code pour effectuer une vérification du type à l'exécution. Si la vérification échoue, une ClassCastException
sera lancée lorsque le code est exécuté.
Notez que tous les types ne sont pas valides. Par exemple:
String s = (String) vehicle; // not valid
Le compilateur Java sait que une instance qui est de type compatible avec Vehicle
ne peut jamais être de type compatible avec String
. Le cast de type n'a jamais pu réussir, et le JLS impose une erreur de compilation.
Programmation à une interface
L'idée derrière la programmation d'une interface est de baser le code principalement sur des interfaces et d'utiliser uniquement des classes concrètes au moment de l'instanciation. Dans ce contexte, un bon code traitant par exemple des collections Java ressemblera à ceci (non pas que la méthode elle-même soit d'aucune utilité, mais seulement une illustration):
public <T> Set<T> toSet(Collection<T> collection) {
return Sets.newHashSet(collection);
}
alors que le code incorrect peut ressembler à ceci:
public <T> HashSet<T> toSet(ArrayList<T> collection) {
return Sets.newHashSet(collection);
}
Non seulement le premier peut être appliqué à un plus grand choix d'arguments, mais ses résultats seront plus compatibles avec le code fourni par d'autres développeurs qui adhèrent généralement au concept de programmation à une interface. Cependant, les raisons les plus importantes pour utiliser le premier sont:
- la plupart du temps, le contexte dans lequel le résultat est utilisé ne nécessite pas et ne devrait pas nécessiter autant de détails que la mise en œuvre concrète le prévoit;
- adhérer à une interface force le code plus propre et moins de piratage, mais une autre méthode publique est ajoutée à une classe desservant un scénario spécifique;
- le code est plus testable car les interfaces sont facilement modifiables;
- enfin, le concept aide même si une seule implémentation est attendue (au moins pour la testabilité).
Alors, comment peut-on facilement appliquer le concept de programmation à une interface lors de l'écriture d'un nouveau code en ayant à l'esprit une implémentation particulière? Une option couramment utilisée est la combinaison des modèles suivants:
- programmation à une interface
- usine
- constructeur
L'exemple suivant basé sur ces principes est une version simplifiée et tronquée d'une implémentation RPC écrite pour différents protocoles:
public interface RemoteInvoker {
<RQ, RS> CompletableFuture<RS> invoke(RQ request, Class<RS> responseClass);
}
L'interface ci-dessus n'est pas supposée être instanciée directement via une fabrique, au lieu de cela nous dérivons d'autres interfaces plus concrètes, une pour l'invocation HTTP et une pour AMQP, chacune ayant une fabrique et un générateur pour construire des instances. l'interface ci-dessus:
public interface AmqpInvoker extends RemoteInvoker {
static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
return new AmqpInvokerBuilder(instanceId, factory);
}
}
Les instances de RemoteInvoker
pour l'utilisation avec AMQP peuvent maintenant être construites aussi facilement (ou plus impliquées selon le constructeur):
RemoteInvoker invoker = AmqpInvoker.with(instanceId, factory)
.requestRouter(router)
.build();
Et l'invocation d'une demande est aussi simple que:
Response res = invoker.invoke(new Request(data), Response.class).get();
Java 8 permettant de placer des méthodes statiques directement dans les interfaces, la fabrique intermédiaire est devenue implicite dans le code ci-dessus remplacé par AmqpInvoker.with()
. En Java avant la version 8, le même effet peut être obtenu avec une classe Factory
interne:
public interface AmqpInvoker extends RemoteInvoker {
class Factory {
public static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
return new AmqpInvokerBuilder(instanceId, factory);
}
}
}
L'instanciation correspondante deviendrait alors:
RemoteInvoker invoker = AmqpInvoker.Factory.with(instanceId, factory)
.requestRouter(router)
.build();
Le générateur utilisé ci-dessus pourrait ressembler à ceci (bien qu'il s'agisse d'une simplification car celle-ci permet de définir jusqu'à 15 paramètres différents des valeurs par défaut). Notez que la construction n'est pas publique, elle n'est donc utilisable que depuis l'interface AmqpInvoker
ci-dessus:
public class AmqpInvokerBuilder {
...
AmqpInvokerBuilder(String instanceId, ConnectionFactory factory) {
this.instanceId = instanceId;
this.factory = factory;
}
public AmqpInvokerBuilder requestRouter(RequestRouter requestRouter) {
this.requestRouter = requestRouter;
return this;
}
public AmqpInvoker build() throws TimeoutException, IOException {
return new AmqpInvokerImpl(instanceId, factory, requestRouter);
}
}
Généralement, un générateur peut également être généré à l'aide d'un outil tel que FreeBuilder.
Enfin, l'implémentation standard (et la seule attendue) de cette interface est définie comme une classe locale de package pour appliquer l'utilisation de l'interface, de la fabrique et du générateur:
class AmqpInvokerImpl implements AmqpInvoker {
AmqpInvokerImpl(String instanceId, ConnectionFactory factory, RequestRouter requestRouter) {
...
}
@Override
public <RQ, RS> CompletableFuture<RS> invoke(final RQ request, final Class<RS> respClass) {
...
}
}
Pendant ce temps, ce modèle s'est avéré très efficace pour développer tout notre nouveau code, quelle que soit la simplicité ou la complexité de la fonctionnalité.
Classe abstraite et utilisation de l'interface: relation "Is-a" vs capacité "Has-a"
Quand utiliser des classes abstraites: Pour implémenter le même comportement ou un comportement différent parmi plusieurs objets associés
Quand utiliser les interfaces: pour implémenter un contrat par plusieurs objets non liés
Les classes abstraites create "is a" relations alors que les interfaces fournissent "a une" capacité.
Cela peut être vu dans le code ci-dessous:
public class InterfaceAndAbstractClassDemo{
public static void main(String args[]){
Dog dog = new Dog("Jack",16);
Cat cat = new Cat("Joe",20);
System.out.println("Dog:"+dog);
System.out.println("Cat:"+cat);
dog.remember();
dog.protectOwner();
Learn dl = dog;
dl.learn();
cat.remember();
cat.protectOwner();
Climb c = cat;
c.climb();
Man man = new Man("Ravindra",40);
System.out.println(man);
Climb cm = man;
cm.climb();
Think t = man;
t.think();
Learn l = man;
l.learn();
Apply a = man;
a.apply();
}
}
abstract class Animal{
String name;
int lifeExpentency;
public Animal(String name,int lifeExpentency ){
this.name = name;
this.lifeExpentency=lifeExpentency;
}
public abstract void remember();
public abstract void protectOwner();
public String toString(){
return this.getClass().getSimpleName()+":"+name+":"+lifeExpentency;
}
}
class Dog extends Animal implements Learn{
public Dog(String name,int age){
super(name,age);
}
public void remember(){
System.out.println(this.getClass().getSimpleName()+" can remember for 5 minutes");
}
public void protectOwner(){
System.out.println(this.getClass().getSimpleName()+ " will protect owner");
}
public void learn(){
System.out.println(this.getClass().getSimpleName()+ " can learn:");
}
}
class Cat extends Animal implements Climb {
public Cat(String name,int age){
super(name,age);
}
public void remember(){
System.out.println(this.getClass().getSimpleName() + " can remember for 16 hours");
}
public void protectOwner(){
System.out.println(this.getClass().getSimpleName()+ " won't protect owner");
}
public void climb(){
System.out.println(this.getClass().getSimpleName()+ " can climb");
}
}
interface Climb{
void climb();
}
interface Think {
void think();
}
interface Learn {
void learn();
}
interface Apply{
void apply();
}
class Man implements Think,Learn,Apply,Climb{
String name;
int age;
public Man(String name,int age){
this.name = name;
this.age = age;
}
public void think(){
System.out.println("I can think:"+this.getClass().getSimpleName());
}
public void learn(){
System.out.println("I can learn:"+this.getClass().getSimpleName());
}
public void apply(){
System.out.println("I can apply:"+this.getClass().getSimpleName());
}
public void climb(){
System.out.println("I can climb:"+this.getClass().getSimpleName());
}
public String toString(){
return "Man :"+name+":Age:"+age;
}
}
sortie:
Dog:Dog:Jack:16
Cat:Cat:Joe:20
Dog can remember for 5 minutes
Dog will protect owner
Dog can learn:
Cat can remember for 16 hours
Cat won't protect owner
Cat can climb
Man :Ravindra:Age:40
I can climb:Man
I can think:Man
I can learn:Man
I can apply:Man
Notes clés:
Animal
est une classe abstraite avec des attributs partagés:name
etlifeExpectancy
et des méthodes abstraites:remember()
etprotectOwner()
.Dog
andCat
sont desAnimals
qui ont implémenté les méthodesremember()
etprotectOwner()
.Cat
peutclimb()
maisDog
ne peut pas.Dog
peutthink()
maisCat
ne peut pas. Ces fonctionnalités spécifiques sont ajoutées àCat
etDog
par implémentation.Man
n'est pas unAnimal
mais il peutThink
,Learn
,Apply
etClimb
.Cat
n'est pas unMan
mais il peutClimb
.Dog
n'est pas unMan
mais il peutLearn
Man
n'est ni unCat
ni unDog
mais peut avoir certaines des capacités de ces deux derniers sans étendreAnimal
,Cat
ouDog
. Ceci est fait avec des interfaces.Même si
Animal
est une classe abstraite, il a un constructeur, contrairement à une interface.
TL; DR:
Les classes non liées peuvent avoir des capacités via des interfaces, mais les classes associées modifient le comportement via l'extension des classes de base.
Reportez-vous à la page de documentation Java pour savoir laquelle utiliser dans un cas d'utilisation spécifique.
Envisagez d'utiliser des classes abstraites si ...
- Vous voulez partager du code entre plusieurs classes étroitement liées.
- Vous vous attendez à ce que les classes qui étendent votre classe abstraite disposent de nombreuses méthodes ou champs communs, ou requièrent des modificateurs d'accès autres que publics (tels que protégés et privés).
- Vous souhaitez déclarer des champs non statiques ou non finaux.
Envisagez d'utiliser des interfaces si ...
- Vous vous attendez à ce que des classes non liées implémentent votre interface. Par exemple, de nombreux objets non liés peuvent implémenter l'interface
Serializable
. - Vous voulez spécifier le comportement d'un type de données particulier mais ne vous préoccupez pas de savoir qui implémente son comportement.
- Vous voulez profiter de l'héritage multiple de type.
Passer outre dans l'héritage
La substitution dans Héritage est utilisée lorsque vous utilisez une méthode déjà définie à partir d'une super-classe dans une sous-classe, mais d'une manière différente de la façon dont la méthode a été conçue à l'origine dans la super-classe. La dérogation permet à l'utilisateur de réutiliser le code en utilisant le matériel existant et en le modifiant pour mieux répondre aux besoins de l'utilisateur.
L'exemple suivant montre comment ClassB
remplace les fonctionnalités de ClassA
en modifiant ce qui est envoyé via la méthode d'impression:
Exemple:
public static void main(String[] args) {
ClassA a = new ClassA();
ClassA b = new ClassB();
a.printing();
b.printing();
}
class ClassA {
public void printing() {
System.out.println("A");
}
}
class ClassB extends ClassA {
public void printing() {
System.out.println("B");
}
}
Sortie:
UNE
B