Quel est un bon cas d'utilisation pour l'importation statique de méthodes?

137

Je viens de recevoir un commentaire sur le fait que mon import statique de la méthode n'était pas une bonne idée. L'importation statique était une méthode d'une classe DA, qui a principalement des méthodes statiques. Donc, au milieu de la logique métier, j'avais une activité da qui semblait appartenir à la classe actuelle:

import static some.package.DA.*;
class BusinessObject {
  void someMethod() {
    ....
    save(this);
  }
} 

Le critique n'a pas voulu que je change le code et je ne l'ai pas fait, mais je suis plutôt d'accord avec lui. L'une des raisons invoquées pour ne pas importer statique était qu'il était déroutant où la méthode a été définie, ce n'était pas dans la classe actuelle et pas dans une superclasse, il était donc trop de temps pour identifier sa définition (le système de révision basé sur le Web n'a pas cliquable liens comme IDE :-) Je ne pense pas vraiment que cela compte, les importations statiques sont encore assez récentes et bientôt nous nous habituerons tous à les localiser.

Mais l'autre raison, celle avec laquelle je suis d'accord, est qu'un appel de méthode non qualifié semble appartenir à l'objet courant et ne doit pas sauter de contextes. Mais si cela appartenait vraiment, il serait logique d'étendre cette super classe.

Alors, quand est- il judicieux d'utiliser des méthodes d'importation statiques? Quand l'avez-vous fait? Avez-vous aimé / avez-vous aimé l'apparence des appels non qualifiés?

EDIT: L'opinion populaire semble être que les méthodes d'importation statique si personne ne va les confondre avec les méthodes de la classe actuelle. Par exemple, les méthodes de java.lang.Math et java.awt.Color. Mais si abs et getAlpha ne sont pas ambigus, je ne vois pas pourquoi readEmployee l'est. Comme dans beaucoup de choix de programmation, je pense que c'est aussi une question de préférence personnelle.

Merci pour votre réponse les gars, je ferme la question.

Variable misérable
la source
2
Voici un très bon usage des importations statiques: ibm.com/developerworks/library/j-ft18
intrepidis
1
@ mr5 la syntaxe est import static, la fonctionnalité eststatic import
Variable misérable

Réponses:

150

Ceci est tiré du guide de Sun lors de la sortie de la fonctionnalité (souligné dans l'original):

Alors, quand devriez-vous utiliser l'importation statique? Très avec parcimonie! Utilisez-le uniquement lorsque vous seriez autrement tenté de déclarer des copies locales de constantes, ou d'abuser de l'héritage (l'antipattern d'interface constante). ... Si vous abusez de la fonctionnalité d'importation statique, cela peut rendre votre programme illisible et non maintenable, polluant son espace de noms avec tous les membres statiques que vous importez. Les lecteurs de votre code (y compris vous, quelques mois après que vous l'avez écrit) ne sauront pas de quelle classe provient un membre statique. L'importation de tous les membres statiques d'une classe peut être particulièrement préjudiciable à la lisibilité; si vous n'avez besoin que d'un ou deux membres, importez-les individuellement.

( https://docs.oracle.com/javase/8/docs/technotes/guides/language/static-import.html )

Il y a deux parties que je veux appeler spécifiquement:

  • N'utilisez les importations statiques que lorsque vous avez été tenté d '«abuser de l'héritage». Dans ce cas, auriez-vous été tenté d'avoir BusinessObject extend some.package.DA? Si tel est le cas, les importations statiques peuvent être un moyen plus propre de gérer cela. Si vous n'auriez jamais rêvé d'étendre some.package.DA, c'est probablement une mauvaise utilisation des importations statiques. Ne l'utilisez pas uniquement pour enregistrer quelques caractères lors de la saisie.
  • Importez des membres individuels. Dites import static some.package.DA.saveau lieu de DA.*. Cela permettra de trouver beaucoup plus facilement d'où provient cette méthode importée.

Personnellement, j'ai utilisé cette fonctionnalité de langage très rarement, et presque toujours uniquement avec des constantes ou des énumérations, jamais avec des méthodes. Le compromis, pour moi, n'en vaut presque jamais la peine.

Ross
la source
9
D'accord. J'ai parfois utilisé des importations statiques où elles ont en fait rendu le code beaucoup plus facile à suivre.
Neil Coffey
65

Une autre utilisation raisonnable des importations statiques est avec JUnit 4. Dans les versions antérieures des méthodes JUnit comme assertEqualset failétaient héritées depuis l'extension de la classe de test junit.framework.TestCase.

// old way
import junit.framework.TestCase;

public class MyTestClass extends TestCase {
    public void myMethodTest() {
        assertEquals("foo", "bar");
    }
}

Dans JUnit 4, les classes de test n'ont plus besoin d'être étendues TestCaseet peuvent à la place utiliser des annotations. Vous pouvez ensuite importer statiquement les méthodes d'assert depuis org.junit.Assert:

// new way
import static org.junit.Assert.assertEquals;

public class MyTestClass {
    @Test public void myMethodTest() {
        assertEquals("foo", "bar");
        // instead of
        Assert.assertEquals("foo", "bar");
    }
}

JUnit documents utilisant cette façon.

Rob Hruska
la source
4
Je suis d'accord. La simplification des cas de test est un endroit où il est peu probable que l'intention soit mal comprise.
Bill Michell le
6
Nous avons eu cela sur notre projet et avons en fait eu des problèmes avec des personnes utilisant assert () et pensant à tort que cela venait de leur importation statique du package Assert. Une fois que nous avons trouvé ce problème, une analyse rapide de notre base de code a trouvé environ 30 instances de ceci dans nos tests, ce qui signifie que 30 assertions n'étaient PAS exécutées lorsque le cadre de test a été exécuté car l'indicateur DEBUG n'est pas défini lorsque nous exécutons des tests.
Chris Williams
27

Effective Java, Second Edition , à la fin de l' article 19, indique que vous pouvez utiliser des importations statiques si vous vous retrouvez à utiliser fortement les constantes d'une classe utilitaire. Je pense que ce principe s'appliquerait aux importations statiques de constantes et de méthodes.

import static com.example.UtilityClassWithFrequentlyUsedMethods.myMethod;

public class MyClass {
    public void doSomething() {
        int foo= UtilityClassWithFrequentlyUsedMethods.myMethod();
        // can be written less verbosely as
        int bar = myMethod();
    }
}

Cela présente des avantages et des inconvénients. Cela rend le code un peu plus lisible au détriment de la perte d'informations immédiates sur l'endroit où la méthode est définie. Cependant, un bon IDE vous permettra d'accéder à la définition, donc ce n'est pas vraiment un problème.

Vous devez toujours l'utiliser avec parcimonie, et seulement si vous vous retrouvez à utiliser des éléments du fichier importé de nombreuses fois.

Edit: mis à jour pour être plus spécifique aux méthodes, car c'est ce à quoi cette question fait référence. Le principe s'applique indépendamment de ce qui est importé (constantes ou méthodes).

Rob Hruska
la source
1
Ma question concerne les méthodes d' importation statique , pas les champs.
Variable misérable
7
Peut-être a-t-il UtilityClassWithFrequentlyUsedMethodsbesoin d'être raccourci.
Steve Kuo
5
@SteveKuo certainement moins que InternalFrameTitlePaneMaximizeButtonWindowNotFocusedState : P
Anirban Nag 'tintinmj'
@ Rob-Hruska Ne pourrais-je pas simplement envelopper une méthode ou un champ d'importation statique dans une nouvelle méthode ou un nouveau champ si je prévois de les utiliser fréquemment? Cela me permettrait-il de ne pas importer statiquement? tels que: double myPI = Math.PI;et puis je peux continuer à me référer au myPIlieu de Math.PI.
Abdul
@Abdul - Ouais, tu pourrais faire ça.
Rob Hruska
14

Je conviens qu'ils peuvent être problématiques du point de vue de la lisibilité et doivent être utilisés avec parcimonie. Mais lorsqu'ils utilisent une méthode statique commune, ils peuvent en fait augmenter la lisibilité. Par exemple, dans une classe de test JUnit, les méthodes telles que leur origine assertEqualssont évidentes. De même pour les méthodes de java.lang.Math.

Joël
la source
5
Et qu'est-ce qui est si mauvais de voir Math.round (d) par rapport à round (d)?
Steve Kuo
5
@SteveKuo - pour la même raison que les mathématiciens utilisent des noms de variables à une lettre lorsqu'ils manipulent des formules: il arrive que des noms plus longs interfèrent avec la lisibilité de l'énoncé global. Prenons une formule impliquant plusieurs fonctions trigonométriques. Une formule mathématique facile à saisir: sin x cos y + cos x sin y. En Java devient: Math.sin(x) * Math.cos(y) + Math.cos(x) * Math.sin(y). Horrible à lire.
ToolmakerSteve
@ToolmakerSteve, c'est pourquoi j'ai usingtellement manqué les directives en C ++: elles peuvent être locales .
Franklin Yu
11

Je pense que l'importation statique est vraiment utile pour supprimer les noms de classe redondants lors de l'utilisation de classes utils comme Arrayset Assertions.

Je ne sais pas pourquoi, mais Ross a sauté la dernière phrase qui mentionne cela dans la documentation à laquelle il fait référence .

Utilisé de manière appropriée, l'importation statique peut rendre votre programme plus lisible, en supprimant le passe-partout de la répétition des noms de classe.

Fondamentalement copié de ce blog: https://medium.com/alphadev-oughtts/static-imports-are-great-but-underused-e805ba9b279f

Donc par exemple:

Assertions dans les tests

C'est le cas le plus évident sur lequel je pense que nous sommes tous d'accord

Assertions.assertThat(1).isEqualTo(2);

// Use static import instead
assertThat(1).isEqualTo(2);

Classes et énumérations d'utils

Le nom de la classe peut être supprimé dans de nombreux cas lors de l'utilisation de classes utils rendant le code plus facile à lire

List<Integer> numbers = Arrays.asList(1, 2, 3);

// asList method name is enough information
List<Integer> numbers = asList(1, 2, 3);

Le package java.time a quelques cas où il devrait être utilisé

// Get next Friday from now, quite annoying to read
LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));

// More concise and easier to read
LocalDate.now().with(next(FRIDAY));

Exemple de non-utilisation

// Ok this is an Optional
Optional.of("hello world");

// I have no idea what this is 
of("hello world");
softarn
la source
10

Je l'utilise beaucoup pour la couleur.

static import java.awt.Color.*;

Il est très peu probable que les couleurs soient confondues avec autre chose.

jjnguy
la source
1
C'est l'un des meilleurs cas d'utilisation que j'ai vus qui diffère de l'ancien JUnit / Hamcrest / TestNG.
kevinarpe
3

Je recommande l'utilisation d' importation statique lors de l' utilisation d' OpenGL avec Java, qui est un cas d' utilisation de tomber dans la « utilisation intensive des constantes d'une classe utilitaire » catégorie

Considérez cela

import static android.opengl.GLES20.*;

vous permet de porter le code C d'origine et d'écrire quelque chose de lisible tel que:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(samplerUniform, 0);
glBindBuffer(GL_ARRAY_BUFFER, vtxBuffer);
glVertexAttribPointer(vtxAttrib, 3, GL_FLOAT, false, 0, 0);

au lieu de cette laideur répandue commune:

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
GLES20.glUniform1i(samplerUniform, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vtxBuffer);
GLES20.glVertexAttribPointer(vtxAttrib, 3, GLES20.GL_FLOAT, false, 0, 0);
Silex
la source
2

Les importations statiques sont la seule «nouvelle» fonctionnalité de Java que je n'ai jamais utilisée et que je n'ai pas l'intention d'utiliser, à cause des problèmes que vous venez de mentionner.

Bombe
la source
Merci Bombe. Eh bien, je pense qu'ils ont plus de sens que d'avoir à étendre et à interfacer qui contient juste un tas de finales statiques.
Variable misérable
2

J'utilise 'import static java.lang.Math. *' Lors du portage de code mathématique lourd de C / C ++ vers java. Les méthodes mathématiques mappent de 1 à 1 et facilitent la différence du code porté sans la qualification de nom de classe.

Fracdroid
la source
2

J'ai trouvé cela très pratique lors de l'utilisation des classes Utility.

Par exemple, au lieu d'utiliser: if(CollectionUtils.isNotEmpty(col))

Je peux à la place:

import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
if(isNotEmpty(col))

Quelle IMO augmente la lisibilité du code lorsque j'utilise cet utilitaire plusieurs fois dans mon code.

Yeikel
la source
2

À propos des tests unitaires: la plupart des gens utilisent des importations statiques pour les différentes méthodes statiques fournies par les frameworks moqueurs , comme when()ou verify().

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

Et bien sûr, lorsque vous utilisez la seule et unique assertThat()assertion, vous devez l'utiliser pour importer statiquement les correspondeurs hamcrest requis, comme dans:

import static org.hamcrest.Matchers.*;
GhostCat
la source
1

Ils sont utiles pour réduire le verbiage, en particulier dans les cas où de nombreuses méthodes importées sont appelées et où la distinction entre les méthodes locales et importées est claire.

Un exemple: code qui implique plusieurs références à java.lang.Math

Autre: une classe de générateur XML où l'ajout du nom de classe à chaque référence masquerait la structure en cours de construction

kdgregory
la source
1

Je pense que les importations statiques sont parfaites pour NLS dans le style gettext.

import static mypackage.TranslatorUtil._;

//...
System.out.println(_("Hello world."));

Cela marque à la fois la chaîne comme une chaîne qui doit être extraite et fournit un moyen simple et propre de remplacer la chaîne par sa traduction.

Matthias Wuttke
la source
1

L'importation statique IMO est une fonctionnalité assez intéressante. Il est absolument vrai qu'une forte dépendance à l'importation statique rend le code illisible et difficile à comprendre à quelle classe appartient une méthode ou un attribut statique. Cependant, d'après mon expérience, cela devient une fonctionnalité utilisable, en particulier lors de la conception de Utilclasses qui fournissent des méthodes et des attributs statiques. L'ambiguïté qui surgit à chaque fois qu'une importation statique peut être contournée en établissant des normes de code. D'après mon expérience au sein d'une entreprise, cette approche est acceptable et rend le code plus propre et facile à comprendre. De préférence, j'insère le _caractère devant les méthodes statiques et les attributs statiques (en quelque sorte adoptés à partir de C). Apparemment, cette approche viole les normes de dénomination de Java, mais elle clarifie le code. Par exemple, si nous avons une classe AngleUtils:

public class AngleUtils {

    public static final float _ZERO = 0.0f;
    public static final float _PI   = 3.14f;

    public static float _angleDiff(float angle1, float angle2){

    }

    public static float _addAngle(float target, float dest){

    }
}

Dans ce cas, l'importation statique apporte de la clarté et la structure du code me semble plus élégante:

import static AngleUtils.*;

public class TestClass{

    public void testAngles(){

        float initialAngle = _ZERO;
        float angle1, angle2;
        _addAngle(angle1, angle2);
    }
}

Tout de suite, quelqu'un peut dire à quelle méthode ou quel attribut provient une importation statique et il cache les informations de la classe à laquelle il appartient. Je ne suggère pas d'utiliser l'importation statique pour les classes qui font partie intégrante d'un module et fournissent des méthodes statiques et non statiques car dans ce cas, il est important de savoir quelle classe fournit certaines fonctionnalités statiques.

Eldjon
la source
Merci pour la suggestion de nommer. BTW, un trait de soulignement devant est traditionnellement utilisé dans certains environnements pour nommer des méthodes / champs privés. J'envisage une convention modifiée, comme H_pour les importations d'une Helperclasse d'utilité que j'ai, ou C_pour Common, ou U_pour Utility. Alternativement, j'ai envisagé d'utiliser un ou deux noms de classes de caractères pour ces classes largement utilisées, mais je craignais que ceux-ci puissent parfois entrer en conflit avec les noms locaux - avoir du code hérité avec des noms de méthode en majuscules.
ToolmakerSteve
-1

Vous devez les utiliser lorsque:

  • vous souhaitez utiliser une switchinstruction avec des valeurs d'énumération
  • vous souhaitez rendre votre code difficile à comprendre
davetron5000
la source
9
Ce n'est pas vrai. (1) Vous pouvez parfaitement utiliser les constantes enum sans importation statique de celles-ci. (2) Les importations statiques, par exemple, de méthodes de classe JUnit Assert sont claires comme une cloche. "assertTrue (...)" est tout aussi lisible que "Assert.assertTrue (...)", peut-être plus.
Alan Krueger le
5
si vous avez 5 importations statiques dans une classe de 500 lignes, il est très difficile de dire d'où viennent les méthodes.
davetron5000
4
+1 pour lorsque vous souhaitez rendre votre code difficile à comprendre :)
Variable misérable
-5

Je les utilise quand je peux. J'ai configuré IntelliJ pour me rappeler si j'oublie. Je pense que cela semble beaucoup plus propre qu'un nom de package complet.

Javamann
la source
13
Vous pensez aux importations régulières. Les importations statiques vous permettent de faire référence aux membres d'une classe sans les qualifier avec un nom de classe, par exemple import statique java.lang.system.out; out.println ("toto"); // au lieu de System.out.println ("foo");
sk.
Maintenant, c'est une très bonne explication des importations statiques ... dommage que je ne puisse pas +1 un commentaire
Eldelshell