web-dev-qa-db-fra.com

Pourquoi pas des champs abstraits?

Pourquoi les classes Java ne peuvent-elles pas avoir des champs abstraits comme elles peuvent avoir des méthodes abstraites?

Par exemple: j'ai deux classes qui étendent la même classe de base abstraite. Ces deux classes ont chacune une méthode identique à l'exception d'une constante String, qui se trouve être un message d'erreur, en leur sein. Si les champs pouvaient être abstraits, je pourrais faire cette constante constante et extraire la méthode dans la classe de base. Au lieu de cela, je dois créer une méthode abstraite, appelée getErrMsg() dans ce cas, qui retourne la chaîne, remplacer cette méthode dans les deux classes dérivées, puis je peux extraire la méthode (qui appelle maintenant la méthode abstraite).

Pourquoi est-ce que je ne pouvais pas simplement faire un résumé de champ pour commencer? Java aurait-il été conçu pour permettre cela?

88
Paul Reiners

Vous pouvez faire ce que vous avez décrit en ayant un dernier champ dans votre classe abstraite qui est initialisé dans son constructeur (code non testé):

abstract class Base {

    final String errMsg;

    Base(String msg) {
        errMsg = msg;
    }

    abstract String doSomething();
}

class Sub extends Base {

    Sub() {
        super("Sub message");
    }

    String doSomething() {

        return errMsg + " from something";
    }
}

Si votre classe enfant "oublie" d'initialiser la finale à travers le super constructeur, le compilateur donnera un avertissement une erreur, tout comme lorsqu'une méthode abstraite n'est pas implémentée.

86
rsp

Je ne vois aucun intérêt à cela. Vous pouvez déplacer la fonction vers la classe abstraite et simplement remplacer un champ protégé. Je ne sais pas si cela fonctionne avec des constantes mais l'effet est le même:

public abstract class Abstract {
    protected String errorMsg = "";

    public String getErrMsg() {
        return this.errorMsg;
    }
}

public class Foo extends Abstract {
    public Foo() {
       this.errorMsg = "Foo";
    }

}

public class Bar extends Abstract {
    public Bar() {
       this.errorMsg = "Bar";
    }
}

Vous voulez donc imposer l'implémentation/la substitution/le type de errorMsg dans les sous-classes? Je pensais que vous vouliez juste avoir la méthode dans la classe de base et que vous ne sachiez pas comment gérer le terrain à ce moment-là.

6
Felix Kling

Il est évident que aurait pu être conçu de manière à permettre cela, mais sous couvertures, il devrait toujours faire un dispatch dynamique, et donc un appel de méthode. La conception de Java (du moins au début) était, dans une certaine mesure, une tentative d'être minimaliste. C'est-à-dire que les concepteurs ont essayé d'éviter d'ajouter de nouvelles fonctionnalités, si celles-ci pouvaient être facilement simulées par d'autres fonctionnalités déjà présentes dans le langage.

3
Laurence Gonsalves

Une autre option consiste à définir le champ en tant que public (final, si vous le souhaitez) dans la classe de base, puis à l'initialiser dans le constructeur de la classe de base, en fonction de la sous-classe actuellement utilisée. C'est un peu louche, en ce sens qu'il introduit une dépendance circulaire. Mais au moins ce n’est pas une dépendance qui peut jamais changer - c’est-à-dire que la sous-classe existera ou n’existera pas, mais les méthodes ou les champs de la sous-classe ne peuvent pas influencer la valeur de field.

public abstract class Base {
  public final int field;
  public Base() {
    if (this instanceof SubClassOne) {
      field = 1;
    } else if (this instanceof SubClassTwo) {
      field = 2;
    } else {
      // assertion, thrown exception, set to -1, whatever you want to do 
      // to trigger an error
      field = -1;
    }
  }
}
0
ragerdl

En lisant votre titre, je pensais que vous faisiez référence aux membres d’instances abstraites; et je ne pouvais pas voir beaucoup d'utilisation pour eux. Mais les membres statiques abstraits est une autre affaire.

J'ai souvent souhaité pouvoir déclarer une méthode semblable à celle-ci en Java:

public abstract class MyClass {

    public static abstract MyClass createInstance();

    // more stuff...

}

En gros, je voudrais insister pour que les implémentations concrètes de ma classe parente fournissent une méthode de fabrique statique avec une signature spécifique. Cela me permettrait d’obtenir une référence à une classe concrète avec Class.forName() et d’être certain de pouvoir en construire une dans une convention de mon choix.

0
Drew Wills