[JAVA] Soyons paresseux avec l'héritage et le remplacement

L'héritage n'est pas bon ~ ...

À l'école, j'ai imaginé une belle succession, alors j'ai parlé de succession avec élan. Puisqu'il est presque ad lib, un résumé est également disponible.

Alors qu'est-ce que l'héritage?

L'héritage signifie qu'en incorporant une autre classe, vous pouvez créer une instance d'une méthode ou d'une variable écrite dans une autre classe et la traiter comme s'il s'agissait d'une méthode écrite dans votre propre classe ou une variable. ..

Utilisez le mot-clé ʻextends` pour l'héritage. La classe héritée est appelée la classe parente (super-classe) et la classe héritée et étendue est appelée la classe enfant (sous-classe).

Classe parent


class SuperClass{
  public int num = 10;

  public void superClassMethod(){
    System.out.println("C'est une classe parent ~");
  }
}

Classe enfant


class SubClass extends SuperClass{
  //Je n'écris rien pour le moment
}

Classe avec méthode principale


class Test{
  public static void main(String[] args){
    SubClass instance = new SubClass();
    instance.superClassMethod();
  }
}

// >>>"C'est une classe parente" s'affiche.

En regardant la relation dans la figure, cela ressemble à ceci. 名称未設定1.png On a l'impression que la classe enfant inclut la classe parent. Si vous pensez en japonais, êtes-vous parent parmi vos enfants? Vous pourriez penser, mais faites de votre mieux et habituez-vous à cela ...

Les classes enfants peuvent utiliser des variables et des méthodes non privées de la classe parent. Si vous souhaitez utiliser des variables privées de la classe parente, préparez les méthodes Getter / Setter.

Classe enfant


class SubClass extends SuperClass{
  void test(){
    //Se référer à la variable de la classe parent
    System.out.println(num);  //10

    //Essayez d'appeler une méthode de la classe parent
    superClassMethod();
  }
}

En héritant de cette façon, j'ai pu donner l'impression d'avoir incorporé une autre classe dans ma classe. Cependant, si vous pouvez utiliser la même méthode à divers endroits, vous pouvez utiliser une méthode de classe.

Ce n'est pas vrai. </ b>

L'héritage permet le remplacement </ b>. essayons.

Allons remplacer

Le remplacement vous permet d'ajouter et de définir des méthodes de superclasse. Pourquoi as-tu besoin de cela? J'en parlerai plus tard, alors attendez une minute.

Classe parent


class SuperClass{
  public void printMethod(){
    System.out.println("C'est une classe parent ~");
  }
}

Classe enfant


class SubClass extends SuperClass{
  @Override
  public void printMethod(){
    System.out.println("Je l'ai remplacé!");
  }
}

De cette manière, la méthode peut être remplacée en définissant une méthode avec le même nom et le même argument que la méthode de la classe parente dans la classe enfant.

L'annotation @ Override est écrite avant la méthode. Ceci pour indiquer au compilateur Java que "cette méthode remplace" et n'a rien à voir avec le comportement du programme. Je ne sais pas s'il sera remplacé sans cela, et si le nom ne correspond pas à la méthode de la classe parente, une erreur sera émise, c'est donc une norme de l'ajouter.

Si vous souhaitez en savoir plus, lisez ce qui suit. Les gens n'ont pas à le lire séparément.

Et si je n'ai pas «@ Override»?

Pourquoi avez-vous besoin de quelque chose qui n'a rien à voir avec le fonctionnement? Par exemple, dans l'exemple, la méthode printMethod () est ajoutée et définie, mais on suppose que le nom de la méthode est priiiiiiiiiintMethod () en raison d'une légère faute de frappe.

Classe enfant


class SubClass extends SuperClass{
  public void priiiiiiiiiintMethod(){
    System.out.println("Je l'ai remplacé!");
  }
}

S'il n'a pas @ Override, il peut être compilé normalement comme une méthode que vous n'essayez pas de remplacer. Si l'appelant de cette méthode fait à nouveau une faute de frappe correcte (?), Elle peut être utilisée normalement, il faut donc du temps pour remarquer que l'opération souhaitée n'est pas effectuée.

Il est préférable d'écrire une annotation @ Override pour éviter une telle erreur humaine. Lorsque vous travaillez dans l'EDI, la fonction de saisie semi-automatique fonctionne bien.

Pourquoi fais-tu une chose aussi gênante?

J'ai pensé: «Si vous voulez en hériter séparément, vous devriez simplement créer une autre classe en la copiant», mais l'héritage est beaucoup plus intelligent qu'on ne le pense.

Parce que vous pouvez réduire le code de copie

Tout d'abord, vous n'avez pas à copier et coller lors de la préparation de classes similaires, ce qui entraîne une réduction de la quantité de code. "Peu importe si c'est long"? Soyons paresseux car c'est ennuyeux de toucher un personnage de plus! !! </ b>

Continuons avec un exemple de création de créatures par programmation.

Essayez d'écrire sans hériter

Chien


class Wanko{
  public void eat(String foodName){
    //Je pensais que c'était écrit sur la façon de manger un chien

    //Sur l'assiette...
    //Direct...
    //Attack!!(Chien mangeant)

    System.out.println(foodName+"a mangé");

    //Digérer
    syouka(foodName);
  }

  public void syouka(String foodName){
    System.out.println(foodName+"Digéré");
  }
}

Singe


class Monkey{
  public void eat(String foodName){
    //Je pensais que le processus de manger des singes était écrit

    //Ramasser à la main...
    //À ta bouche...
    //Je veux que vous le portiez et que vous le mangiez.

    System.out.println(foodName+"a mangé");

    //Digérer
    syouka(foodName);
  }

  public void syouka(String foodName){
    System.out.println(foodName+"Digéré");
  }
}

Maintenant, il y a quelques similitudes avec ces deux méthodes. Réduisons la quantité de code en héritant de cela.

Hériter et écrire

Commencez par créer une classe de créature </ b> de base. Je ne suis pas biologiste, donc je ne sais pas, mais la procédure de digestion des singes et des chiens est la même.

Créatures


class Animal{
  public void eat(String foodName){
    System.out.println(foodName+"a mangé");

    //Processus de digestion
    syouka(foodName);
  }
  public void syouka(String foodName){
    System.out.println(foodName+"Digéré");
  }
}

Héritons de cela et écrivons une classe de chien et une classe de singe.

Chien


class Wanko extends Animal{
  @Override
  public void eat(String foodName){
    //Je pensais que c'était écrit sur la façon de manger un chien
    //Sur l'assiette...
    //Direct...
    //Attack!!(Chien mangeant)

    //super()Peut être utilisé pour appeler la méthode eat de la classe parent.
    super(foodName);
  }
}

Singe


class Monkey extends Animal{
  @Override
  public void eat(String foodName){
    //Je pensais que le processus de manger des singes était écrit

    //Ramasser à la main...
    //À ta bouche...
    //Je veux que vous le portiez et que vous le mangiez.

    //super()Peut être utilisé pour appeler la méthode eat de la classe parent.
    super(foodName);
  }
}

Soudain, j'ai écrit «super ();», mais cela n'est jamais arrivé. Il appelle simplement la méthode de la classe parent. La méthode parent class ʻeat () `</ b> est utilisée pour afficher ce qui a été mangé et digéré, ce qui est un processus courant pour les chiens et les singes.

Au fait ... (À propos de super ())

super () est un mot clé qui peut être utilisé dans le constructeur de la classe enfant parmi les méthodes surchargées, et la méthode et le constructeur de la classe parent peuvent être appelés. super () peut aussi passer des arguments et surcharger comme les méthodes normales.

mot-clé abstrait

La classe ʻAnimal susmentionnée était une classe <b> qui supposait l'héritage </ b>. Si tel est le cas, la classe ʻAnimal peut être utilisée en créant une instance sans héritage, mais ce n'est pas une classe créée à cet effet.

Dans un tel cas, utilisez le mot-clé ʻabstract. ʻAbstract est également attaché aux classes et aux méthodes.

ʻAbstract` signifie abstrait </ b>, intangible </ b>. Comme son nom l'indique, il s'agit d'une classe et méthode abstraite </ b>, veuillez donc la rendre concrète avant de l'utiliser.

Si ʻabstract est ajouté à la classe, l'instanciation par le mot-clé new est interdite et elle ne peut pas être utilisée à moins que l'héritage <b> ne soit effectué. </ b> Les classes avec ʻabstract sont appelées classes abstraites </ b>.

Si ʻabstract` est ajouté à la méthode, le processus ne peut pas être décrit et la classe enfant est forcée de se substituer. </ b> Si vous ne le remplacez pas par la classe enfant, il ne sera pas compilé. De plus, bien entendu, «super ()» ne peut pas être utilisé.

Créatures


class abstract Animal{
  public void eat(String foodName){
    System.out.println(foodName+"a mangé");

    //Processus de digestion
    syouka(foodName);
  }
  public void syouka(String foodName){
    System.out.println(foodName+"Digéré");
  }

  public abstract void printName();
}

Singe


class Monkey extends Animal{
  @Override
  public void eat(String foodName){
    //Je pensais que le processus de manger des singes était écrit

    //Ramasser à la main...
    //À ta bouche...
    //Je veux que vous le portiez et que vous le mangiez.

    //super()Peut être utilisé pour appeler la méthode eat de la classe parent.
    super(foodName);
  }

  //printName()Vous devez toujours remplacer la méthode.
  @Override
  public void printName(){
    System.out.println("Wai est un singe");
  }
}

Mot-clé final

Vous pouvez ajouter final pour interdire d'autres modifications. ..

Lorsqu'il est attaché à une classe, l'héritage est interdit. Lorsqu'il est attaché à une méthode, le remplacement est interdit. Lorsqu'elle est attachée à une variable, la réaffectation d'une valeur autre que lorsque la variable est déclarée est interdite.

Parce qu'il peut être traité comme un type de classe parent

Il est vraiment pratique de pouvoir effectuer une conversion ascendante vers le type de classe parent </ b>.

Ramenez à nouveau les animaux susmentionnés.

Créatures


class Animal{
  public void eat(String foodName){
    System.out.println(foodName+"a mangé");

    //Processus de digestion
    syouka(foodName);
  }
  public void syouka(String foodName){
    System.out.println(foodName+"Digéré");
  }
}

Chien


class Wanko extends Animal{
  @Override
  public void eat(String foodName){
    //Je pensais que c'était écrit sur la façon de manger un chien
    //Sur l'assiette...
    //Direct...
    //Attack!!(Chien mangeant)

    //super()Peut être utilisé pour appeler la méthode eat de la classe parent.
    super(foodName);
  }
}

Singe


class Monkey extends Animal{
  @Override
  public void eat(String foodName){
    //Je pensais que le processus de manger des singes était écrit

    //Ramasser à la main...
    //À ta bouche...
    //Je veux que vous le portiez et que vous le mangiez.

    //super()Peut être utilisé pour appeler la méthode eat de la classe parent.
    super(foodName);
  }
}

L'animal, le chien et le singe ont une relation parent-enfant. Si vous avez une relation parent-enfant, vous pouvez effectuer une conversion de type ...! </ b>

Tout d'abord, souhaitez-vous l'écrire sans conversion de type?

class MeshiKue{
  public static void main(String[] args){
    Wanko dog = new Wanko();
    Monkey monkey = new Monkey();

    Gohan(dog);
    Gohan(monkey);
  }

  void Gohan(Wanko wanko){
    wanko.eat("pomme");
  }

  void Gohan(Monkey monkey){
    monkey.eat("pomme");
  }
}   

Eh bien, ce sera comme ça. Ce n'est pas grave car il existe deux types, mais si le nombre d'amis augmente et que le nombre de chats servals, Arai-san et Fennec augmente, alors la méthode Gohan () devra être écrite pour chaque type. </ B> b> Droite.

Si c'est le même processus, vous ne pourrez pas le voir, alors utilisons la conversion de type pour n'en faire qu'un. </ b>

class MeshiKue{
  public static void main(String[] args){
    Wanko dog = new Wanko();
    Monkey monkey = new Monkey();

    //Upcast chiens et singes au type animal
    Gohan((Animal)dog);
    Gohan((Animal)monkey);
  }

  //Seulement un!
  void Gohan(Animal animal){
    animal.eat("pomme");
  }
}   

Le cast d'un type de classe enfant en type de classe parent s'appelle upcast </ b>, et le cast d'un type de classe parent en un type de classe enfant est appelé downcast </ b>. Le downcasting ne peut être effectué que sur les variables qui ont été remontées une fois.

S'il existe une relation parent-enfant, la classe enfant aura toujours les méthodes et les variables incluses dans la classe parent, afin qu'elle puisse être convertie en classe parent.

Même s'il existe une méthode avec le même nom et un argument formel entre deux classes qui n'ont pas de relation parent-enfant, il se trouve juste qu'il existe une méthode avec le nom et l'argument formel </ b>, donc c'est statique. La conversion de type ne peut pas être effectuée dans une langue saisie.

Au fait

Avec un langage typé dynamiquement tel que Python, le typage canard </ b> vous permet de le faire même si vous avez une méthode avec le même nom et qu'il n'y a pas de relation parent-enfant. Le langage typé dynamiquement s'exécute ligne par ligne sur une base aléatoire, il ne doit donc exister qu'à ce moment-là.

à la fin

Mendokuseee d'écriture de programme! Si vous pensez </ b>, si vous cherchez une fonction de langage qui peut le résoudre, ou s'il existe une syntaxe plus courte, je crois que vous vous rapprocherez de la race appelée un bon programmeur, alors faisons de notre mieux pour la trouver. !! !!