Je comprends cela dans ce code:
class Foo {
public static void method() {
System.out.println("in Foo");
}
}
class Bar extends Foo {
public static void method() {
System.out.println("in Bar");
}
}
.. la méthode statique dans Bar
'masque' la méthode statique déclarée dans Foo
, par opposition à la remplacer dans le sens du polymorphisme.
class Test {
public static void main(String[] args) {
Foo.method();
Bar.method();
}
}
... affichera:
à Foo
en barre
Redéfinir method()
en tant que final
dans Foo
désactivera la possibilité pour Bar
de le masquer et la ré-exécution de main()
générera:
à Foo
dans Foo
( Edit : la compilation échoue lorsque vous marquez la méthode avec final
et ne s'exécute à nouveau que lorsque je supprime Bar.method()
)}
Est-il considéré comme une mauvaise pratique de déclarer des méthodes statiques avec la variable final
si elle empêche les sous-classes de redéfinir la méthode de manière intentionnelle ou par inadvertance?
( this est une bonne explication du comportement de l'utilisation de final
..)
Je ne considère pas comme une mauvaise pratique de marquer une méthode static
comme final
.
Comme vous l'avez découvert, final
empêchera la méthode d'être cachée par les sous-classes ce qui est une très bonne nouvelle imho .
Je suis assez surpris par votre déclaration:
Redéfinir method () comme final dans Foo désactive la possibilité de le masquer par Bar, et la réexécution de main () produira:
à Foo
dans Foo
Non, marquer la méthode avec final
dans Foo
empêchera Bar
de compiler. Au moins dans Eclipse, je reçois:
Exception dans le fil "principal" Java.lang.Error: Problème de compilation non résolu: Impossible de remplacer la méthode finale de Foo
De plus, je pense que les gens devraient toujours invoquer la méthode static
en les qualifiant avec le nom de la classe, même dans la classe elle-même:
class Foo
{
private static final void foo()
{
System.out.println("hollywood!");
}
public Foo()
{
foo(); // both compile
Foo.foo(); // but I prefer this one
}
}
Les méthodes statiques sont l’une des fonctionnalités les plus déroutantes de Java. Les meilleures pratiques existent pour résoudre ce problème, et rendre toutes les méthodes statiques final
est l'une de ces meilleures pratiques!
Le problème avec les méthodes statiques est que
donc vous devriez
final
et vous devriez
NB: la deuxième version de votre programme devrait échouer une erreur de compilation. Je suppose que votre IDE vous cache ce fait!
Si j'ai une méthode public static
, elle est souvent déjà située dans une soi-disant classe d'utilitaires} avec uniquement des méthodes static
. Les exemples auto-explicatifs sont StringUtil
, SqlUtil
, IOUtil
, etc. Ces classes d'utilitaires sont déjà déclarées final
et sont fournies avec un constructeur private
. Par exemple.
public final class SomeUtil {
private SomeUtil() {
// Hide c'tor.
}
public static SomeObject doSomething(SomeObject argument1) {
// ...
}
public static SomeObject doSomethingElse(SomeObject argument1) {
// ...
}
}
De cette façon, vous ne pouvez pas les remplacer.
Si le vôtre ne se trouve pas dans le genre d'une classe d'utilitaires, je remettrais en question la valeur du modificateur public
. Ne devrait-il pas être private
? Sinon, déplacez-le vers une classe utilitaire. Ne pas encombrer les classes "normales" avec les méthodes public static
. De cette façon, vous n'avez pas non plus besoin de les marquer final
.
Un autre cas est une sorte de classe de fabrique abstraite, qui renvoie des implémentations concrètes de self via une méthode public static
. Dans un tel cas, il serait parfaitement logique de marquer la méthode final
, vous ne voulez pas que les implémentations concrètes puissent remplacer la méthode.
Habituellement, avec des classes d'utilitaires (classes avec uniquement des méthodes statiques), il n'est pas souhaitable d'utiliser l'héritage. Pour cette raison, vous voudrez peut-être définir la classe en tant que finale pour empêcher d'autres classes de l'étendre. Cela empêcherait de placer des modificateurs finaux sur vos méthodes de classe d'utilitaires.
Le code ne compile pas:
Test.Java:8: method () dans Bar ne peut pas Surcharger method () dans Foo; surchargée méthode est statique finale public static void, méthode () {
Le message est trompeur car une méthode statique peut, par définition, ne jamais être remplacée.
Je fais ce qui suit lors du codage (pas à 100% tout le temps, mais rien ici n’est "faux":
(La première série de "règles" est faite pour la plupart des choses - certains cas spéciaux sont couverts après)
Puisqu'une interface ne peut pas avoir de méthodes statiques, le problème n'est pas résolu. Si vous voulez créer des méthodes statiques dans la classe abstraite ou dans des classes concrètes, elles doivent être privées, il n’ya aucun moyen de les remplacer.
Cas spéciaux:
Classes utilitaires (classes avec toutes les méthodes statiques):
Si vous voulez avoir une méthode statique dans une classe concrète ou abstraite qui n'est pas privée, vous voudrez probablement plutôt créer une classe utilitaire.
Les classes de valeur (une classe très spécialisée pour essentiellement contenir des données, comme Java.awt.Point où elle contient à peu près les valeurs x et y):
Si vous suivez les conseils ci-dessus, vous obtiendrez un code assez flexible, avec une séparation assez nette des responsabilités.
Un exemple de classe de valeur est cette classe d'emplacement:
import Java.util.ArrayList;
import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;
public final class Location
implements Comparable<Location>
{
// should really use weak references here to help out with garbage collection
private static final Map<Integer, Map<Integer, Location>> locations;
private final int row;
private final int col;
static
{
locations = new HashMap<Integer, Map<Integer, Location>>();
}
private Location(final int r,
final int c)
{
if(r < 0)
{
throw new IllegalArgumentException("r must be >= 0, was: " + r);
}
if(c < 0)
{
throw new IllegalArgumentException("c must be >= 0, was: " + c);
}
row = r;
col = c;
}
public int getRow()
{
return (row);
}
public int getCol()
{
return (col);
}
// this ensures that only one location is created for each row/col pair... could not
// do that if the constructor was not private.
public static Location fromRowCol(final int row,
final int col)
{
Location location;
Map<Integer, Location> forRow;
if(row < 0)
{
throw new IllegalArgumentException("row must be >= 0, was: " + row);
}
if(col < 0)
{
throw new IllegalArgumentException("col must be >= 0, was: " + col);
}
forRow = locations.get(row);
if(forRow == null)
{
forRow = new HashMap<Integer, Location>(col);
locations.put(row, forRow);
}
location = forRow.get(col);
if(location == null)
{
location = new Location(row, col);
forRow.put(col, location);
}
return (location);
}
private static void ensureCapacity(final List<?> list,
final int size)
{
while(list.size() <= size)
{
list.add(null);
}
}
@Override
public int hashCode()
{
// should think up a better way to do this...
return (row * col);
}
@Override
public boolean equals(final Object obj)
{
final Location other;
if(obj == null)
{
return false;
}
if(getClass() != obj.getClass())
{
return false;
}
other = (Location)obj;
if(row != other.row)
{
return false;
}
if(col != other.col)
{
return false;
}
return true;
}
@Override
public String toString()
{
return ("[" + row + ", " + col + "]");
}
public int compareTo(final Location other)
{
final int val;
if(row == other.row)
{
val = col - other.col;
}
else
{
val = row - other.row;
}
return (val);
}
}
Il peut être intéressant de marquer les méthodes statiques comme définitives, en particulier si vous développez un cadre que vous attendez d’autres extensions. Ainsi, vos utilisateurs ne cacheront pas par inadvertance vos méthodes statiques dans leurs classes. Mais si vous développez un framework, vous voudrez peut-être éviter d'utiliser des méthodes statiques pour commencer.
La plupart de ce problème final
remonte à l'époque où les VM étaient plutôt stupides/conservatrices. À l'époque, si vous aviez marqué une méthode final
, cela signifiait (entre autres) que le VM pouvait la mettre en ligne, évitant ainsi les appels de méthode. Ce n'est pas le cas depuis un temps long, long (ou long double: P): http://Java.Sun.com/developer/technicalArticles/Networking/HotSpot/inlining.html .
Isupposel'inspection Idea/Netbeans vous avertit, car elle pense que vous souhaitez utiliser le mot clé final
pour l'optimisation et vous pensez que vous ignorez le fait qu'elle n'est pas nécessaire. VM modernes.
Juste mes deux cents...
J'ai rencontré un inconvénient à l'utilisation des méthodes finales utilisant AOP et MVC de Spring. J'essayais d'utiliser l'AOP de Spring dans les crochets de sécurité autour de l'une des méthodes de AbstractFormController, qui a été déclarée finale. Je pense que spring utilisait la bibliothèque bcel pour l'injection en classe et qu'il y avait certaines limites.
Parce que les méthodes statiques sont les propriétés de la classe et qu’elles sont appelées avec le nom de la classe plutôt que de l’objet. Si nous rendons également la méthode de la classe parent finale, elle ne sera pas surchargée, car les méthodes finales ne permettent pas de changer l'emplacement de la mémoire, mais nous pouvons mettre à jour le membre de données final au même emplacement de la mémoire ...
Lorsque je crée des classes d'utilitaires pures, je déclare alors avec un constructeur privé afin qu'elles ne puissent pas être étendues. Lors de la création de classes normales, je déclare mes méthodes statiques si elles n'utilisent aucune des variables d'instance de classe (ou, dans certains cas, même si elles l'étaient, je transmettrais les arguments de la méthode et la rendrais statique, il est plus facile de voir ce que fait la méthode). Ces méthodes sont déclarées statiques mais sont également privées. Elles existent uniquement pour éviter la duplication de code ou pour rendre le code plus facile à comprendre.
Ceci étant dit, je ne me souviens pas d'avoir rencontré une classe qui possède des méthodes statiques publiques et qui peut/devrait être étendue. Mais, sur la base de ce qui a été rapporté ici, je déclarerais ses méthodes statiques comme définitives.