Méthode Java `final`: que promet-elle?

141

Dans une classe Java, une méthode peut être définie comme étant final, pour marquer que cette méthode ne peut pas être remplacée:

public class Thingy {
    public Thingy() { ... }
    public int operationA() {...}
    /** this method does @return That and is final. */
    public final int getThat() { ...}
}

C'est clair, et cela peut être d'une certaine utilité pour se protéger contre un dépassement accidentel, ou peut-être des performances - mais ce n'est pas ma question.

Ma question est la suivante: d'un point de vue POO, j'ai compris qu'en définissant une méthode, finalle concepteur de classe promet que cette méthode fonctionnera toujours comme décrit ou sous-entendu. Mais souvent, cela peut être hors de l'influence de l'auteur de la classe, si ce que fait la méthode est plus compliqué, alors il suffit de fournir une propriété .

La contrainte syntaxique est claire pour moi, mais quelle est l'implication au sens de la POO? Est-il finalutilisé correctement dans ce sens par la plupart des auteurs de classe?

Quel genre de «contrat» une finalméthode promet-elle?

Towi
la source

Réponses:

156

Comme mentionné, finalest utilisé avec une méthode Java pour marquer que la méthode ne peut pas être remplacée (pour la portée de l'objet) ou masquée (pour la statique). Cela permet au développeur d'origine de créer des fonctionnalités qui ne peuvent pas être modifiées par des sous-classes, et c'est toute la garantie qu'il offre.

Cela signifie que si la méthode repose sur d'autres composants personnalisables tels que des champs / méthodes non publics, la fonctionnalité de la méthode finale peut toujours être personnalisable. C'est bien car (avec le polymorphisme) cela permet une personnalisation partielle.

Il existe un certain nombre de raisons pour empêcher que quelque chose soit personnalisable, notamment:

  • Performances - Certains compilateurs peuvent analyser et optimiser l'opération, en particulier celle sans effets secondaires.

  • Obtenez des données encapsulées - regardez les objets immuables dont les attributs sont définis au moment de la construction et ne doivent jamais être modifiés. Ou une valeur calculée dérivée de ces attributs. Un bon exemple est la Stringclasse Java .

  • Fiabilité et contrat - Les objets sont composés de primitives ( int, char, double, etc.) et / ou d' autres objets. Toutes les opérations applicables à ces composants ne doivent pas être applicables ou même logiques lorsqu'elles sont utilisées dans le plus grand objet. Des méthodes avec le finalmodificateur peuvent être utilisées pour garantir cela. La classe Counter est un bon exemple.


public class Counter {
    private int counter = 0;

    public final int count() {
        return counter++;
    }

    public final int reset() {
        return (counter = 0);
    }
}

Si la public final int count()méthode ne l'est pas final, nous pouvons faire quelque chose comme ceci:

Counter c = new Counter() {   
    public int count() {
        super.count();   
        return super.count();   
    } 
}

c.count(); // now count 2

Ou quelque chose comme ça:

Counter c = new Counter() {
    public int count() {
        int lastCount = 0;
        for (int i = super.count(); --i >= 0; ) {
            lastCount = super.count();
        }

        return lastCount;
    }
}

c.count(); // Now double count
NawaMan
la source
27

Quel type de «contrat» une méthode finale promet-elle?

Regardez les choses dans l'autre sens, toute méthode non finale donne la garantie implicite que vous pouvez la remplacer par votre propre implémentation et que la classe fonctionnera toujours comme prévu. Lorsque vous ne pouvez pas garantir que votre classe prend en charge l'écrasement d'une méthode, vous devez la rendre définitive.

josefx
la source
Mais ce point de vue n'implique-t-il pas à ma question initiale «cette méthode finale se comportera toujours comme promis» que je ne dois pas appeler de méthodes non finales depuis l'intérieur d'une méthode finale? Parce que, si je fais la méthode appelée peut avoir été remplacée et donc je ne peux pas garantir le comportement de ma méthode finale?
towi
8

Tout d'abord, vous pouvez marquer des classes non abstraites finalainsi que des champs et des méthodes. De cette façon, toute la classe ne peut pas être sous-classée. Ainsi, le comportement de la classe sera corrigé.

Je suis d'accord que les méthodes de marquage finalne garantissent pas que leur comportement sera le même dans les sous-classes si ces méthodes appellent des méthodes non finales. Si le comportement doit effectivement être corrigé, cela doit être réalisé par convention et conception soignée. Et n'oubliez pas de le comprendre dans javadoc! (Documentation java)

Dernier point mais non le moindre, le finalmot - clé a un rôle très important dans Java Memory Model (JMM). Il est garanti par JMM que pour obtenir la visibilité des finalchamps, vous n'avez pas besoin d'une synchronisation appropriée. Par exemple:

class A implements Runnable {
  final String caption = "Some caption";                           

  void run() {
    // no need to synchronize here to see proper value of final field..
    System.out.println(caption);
  }
}  
Victor Sorokin
la source
oui je connais les classes finales - cas facile. Bon point sur les champs finaux avec JMM, aucune synchronisation nécessaire ... hmm: cela ne fait référence qu'au "pointeur", non? Je pourrais toujours modifier le désynchronisation de l'objet auquel il fait référence (ok, pas dans String, mais dans les classes définies par l'utilisateur). Mais ce que vous avez dit à propos du "final ne garantit pas le comportement" est exactement ce que je veux dire . Je suis d'accord, la doc et le design sont importants.
2011
@owi vous avez raison qui finaln'assurera pas la visibilité des modifications apportées aux objets composés, tels que Map.
Victor Sorokin le
0

Je ne suis pas sûr que vous puissiez faire des affirmations sur l'utilisation de «final» et comment cela influe sur le contrat de conception global du logiciel. Vous êtes assuré qu'aucun développeur ne peut ignorer cette méthode et annuler son contrat de cette façon. Mais d'un autre côté, la méthode finale peut s'appuyer sur des variables de classe ou d'instance dont les valeurs sont définies par des sous-classes, et peut appeler d'autres méthodes de classe qui sont surchargées. La garantie finale est donc tout au plus très faible.

Jim Ferrans
la source
1
Oui, c'est ce que je voulais dire. Droite. J'aime le terme «garantie faible» :-) Et j'aime (surtout) le C ++ const. Comme dans char const * const = "Hello"ou char const * const addName(char const * const name) const...
Towi
0

Non, ce n'est pas en dehors de l'influence de l'auteur de la classe. Vous ne pouvez pas le remplacer dans votre classe dérivée, donc il fera ce que l'auteur de la classe de base a prévu.

http://download.oracle.com/javase/tutorial/java/IandI/final.html

Il convient de noter la partie où il suggère que les méthodes appelées à partir des constructeurs devraient être final.

Brian Roach
la source
1
Eh bien, techniquement, cela fera ce que l'auteur de la classe de base a écrit .
Joey le