Was ist ein guter Anwendungsfall für den statischen Import von Methoden?

137

Ich habe gerade einen Kommentar erhalten, dass mein statischer Import der Methode keine gute Idee war. Der statische Import erfolgte von einer Methode aus einer DA-Klasse, die hauptsächlich statische Methoden enthält. Mitten in der Geschäftslogik hatte ich eine Aktivität, die anscheinend zur aktuellen Klasse zu gehören schien:

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

Der Rezensent war nicht daran interessiert, dass ich den Code ändere, und ich habe es nicht getan, aber ich stimme ihm irgendwie zu. Ein Grund dafür, dass der statische Import nicht statisch war, war verwirrend, wo die Methode definiert wurde, sie war nicht in der aktuellen Klasse und nicht in einer Oberklasse, so dass es zu einige Zeit dauerte, ihre Definition zu identifizieren (das webbasierte Überprüfungssystem ist nicht anklickbar Links wie IDE :-) Ich denke nicht wirklich, dass dies wichtig ist, statische Importe sind noch ziemlich neu und bald werden wir uns alle daran gewöhnen, sie zu finden.

Der andere Grund, dem ich zustimme, ist, dass ein nicht qualifizierter Methodenaufruf zum aktuellen Objekt zu gehören scheint und keine Kontexte überspringen sollte. Aber wenn es wirklich dazu gehörte, wäre es sinnvoll, diese Superklasse zu erweitern.

Also, wenn nicht es sinnvoll, statische Import - Methoden machen? Wann hast du es getan? Hat / hat Ihnen das Aussehen der unqualifizierten Anrufe gefallen?

EDIT: Die populäre Meinung scheint zu sein, dass statische Importmethoden, wenn niemand sie als Methoden der aktuellen Klasse verwechseln wird. Zum Beispiel Methoden aus java.lang.Math und java.awt.Color. Aber wenn abs und getAlpha nicht mehrdeutig sind, verstehe ich nicht, warum readEmployee ist. Wie bei vielen Programmieroptionen denke auch ich, dass dies eine persönliche Präferenzsache ist.

Vielen Dank für Ihre Antwort, ich schließe die Frage.

Elende Variable
quelle
2
Hier ist eine sehr gute Verwendung von statischen Importen: ibm.com/developerworks/library/j-ft18
intrepidis
1
@ mr5 die Syntax ist import static, die Funktion iststatic import
Miserable Variable

Antworten:

150

Dies ist aus Suns Leitfaden, als sie die Funktion veröffentlichten (Hervorhebung im Original):

Wann sollten Sie den statischen Import verwenden? Sehr sparsam! Verwenden Sie es nur, wenn Sie sonst versucht wären, lokale Kopien von Konstanten zu deklarieren oder die Vererbung zu missbrauchen (das Antipattern der Konstantenschnittstelle). ... Wenn Sie die statische Importfunktion überbeanspruchen, kann dies dazu führen, dass Ihr Programm unlesbar und nicht mehr wartbar ist und sein Namespace mit allen importierten statischen Elementen verschmutzt wird. Leser Ihres Codes (einschließlich Sie, einige Monate nachdem Sie ihn geschrieben haben) wissen nicht, aus welcher Klasse ein statisches Mitglied stammt. Das Importieren aller statischen Elemente aus einer Klasse kann die Lesbarkeit besonders beeinträchtigen. Wenn Sie nur ein oder zwei Mitglieder benötigen, importieren Sie diese einzeln.

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

Es gibt zwei Teile, die ich speziell hervorheben möchte:

  • Verwenden Sie statische Importe nur, wenn Sie versucht waren, "Vererbung zu missbrauchen". Wären Sie in diesem Fall versucht gewesen, BusinessObject zu haben extend some.package.DA? In diesem Fall können statische Importe eine sauberere Methode sein, um dies zu handhaben. Wenn Sie nie von einer Erweiterung geträumt hätten some.package.DA, ist dies wahrscheinlich eine schlechte Verwendung statischer Importe. Verwenden Sie es nicht nur, um beim Tippen ein paar Zeichen zu speichern.
  • Einzelne Mitglieder importieren. Sagen Sie import static some.package.DA.savestatt DA.*. Dadurch ist es viel einfacher festzustellen, woher diese importierte Methode stammt.

Persönlich habe ich diese Sprachfunktion sehr selten und fast immer nur mit Konstanten oder Aufzählungen verwendet, niemals mit Methoden. Der Kompromiss ist für mich fast nie wert.

Ross
quelle
9
Einverstanden. Ich habe gelegentlich statische Importe verwendet, bei denen der Code erheblich einfacher zu befolgen ist.
Neil Coffey
65

Eine weitere sinnvolle Verwendung für statische Importe ist JUnit 4. In früheren Versionen von JUnit wurden assertEqualsund failwurden Methoden wie und geerbt, seit die Testklasse erweitert wurde junit.framework.TestCase.

// old way
import junit.framework.TestCase;

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

In JUnit 4 müssen Testklassen nicht mehr erweitert werden TestCaseund können stattdessen Anmerkungen verwenden. Sie können die Assert-Methoden dann statisch importieren aus 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- Dokumente verwenden es auf diese Weise.

Rob Hruska
quelle
4
Ich würde zustimmen. Die Vereinfachung von Testfällen ist ein Ort, an dem die Absicht wahrscheinlich nicht missverstanden wird.
Bill Michell
6
Wir hatten dies in unserem Projekt und hatten tatsächlich Probleme mit Leuten, die assert () verwenden und fälschlicherweise denken, dass es von ihrem statischen Import des Assert-Pakets herrührt. Sobald wir dieses Problem gefunden haben, hat ein schneller Scan unserer Codebasis in unseren Tests etwa 30 Instanzen davon gefunden, was bedeutet, dass 30 Assertions NICHT ausgeführt wurden, als das Testframework ausgeführt wurde, da das DEBUG-Flag beim Ausführen von Tests nicht gesetzt ist.
Chris Williams
27

Effective Java, Second Edition , am Ende von Punkt 19 stellt fest , dass Sie statische Importe verwenden können , wenn Sie sich selbst zu finden stark Konstanten von einer Dienstprogramm - Klasse. Ich denke, dieses Prinzip würde für statische Importe von Konstanten und Methoden gelten.

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();
    }
}

Dies hat Vor- und Nachteile. Dadurch wird der Code auf Kosten des Verlusts sofortiger Informationen darüber, wo die Methode definiert ist, etwas lesbarer. Mit einer guten IDE können Sie jedoch zur Definition wechseln, sodass dies kein großes Problem darstellt.

Sie sollten dies immer noch sparsam verwenden und nur dann, wenn Sie feststellen, dass Sie Dinge aus der importierten Datei viele, viele Male verwenden.

Bearbeiten: Aktualisiert, um spezifischer für Methoden zu sein, da sich diese Frage darauf bezieht. Das Prinzip gilt unabhängig davon, was importiert wird (Konstanten oder Methoden).

Rob Hruska
quelle
1
Meine Frage betrifft statisch importierende Methoden , keine Felder.
Elende Variable
7
Muss vielleicht UtilityClassWithFrequentlyUsedMethodsgekürzt werden.
Steve Kuo
5
@SteveKuo sicherlich weniger als InternalFrameTitlePaneMaximizeButtonWindowNotFocusedState : P
Anirban Nag 'tintinmj'
@ Rob-Hruska Könnte ich nicht einfach eine statische Importmethode oder ein statisches Feld in eine neue Methode oder ein neues Feld einbinden, wenn ich vorhabe, sie häufig zu verwenden? Würde es mir erlauben, nicht statisch zu importieren? wie zum Beispiel: double myPI = Math.PI;und dann kann ich mich einfach weiter beziehen myPIanstatt Math.PI.
Abdul
@Abdul - Ja, das könntest du machen.
Rob Hruska
14

Ich stimme zu, dass sie aus Sicht der Lesbarkeit problematisch sein können und sparsam eingesetzt werden sollten. Bei Verwendung einer gängigen statischen Methode können sie jedoch die Lesbarkeit verbessern. In einer JUnit-Testklasse sind beispielsweise Methoden wie assertEqualsoffensichtlich, woher sie stammen. Ähnliches gilt für Methoden aus java.lang.Math.

Joel
quelle
5
Und was ist so schlimm daran, Math.round (d) gegen round (d) zu sehen?
Steve Kuo
5
@SteveKuo - aus dem gleichen Grund, aus dem Mathematiker bei der Bearbeitung von Formeln Ein-Buchstaben-Variablennamen verwenden: Es gibt Zeiten, in denen längere Namen die Lesbarkeit der Gesamtanweisung beeinträchtigen. Stellen Sie sich eine Formel mit mehreren trigonometrischen Funktionen vor. Eine leicht verständliche mathematische Formel : sin x cos y + cos x sin y. In Java wird : Math.sin(x) * Math.cos(y) + Math.cos(x) * Math.sin(y). Schrecklich zu lesen.
ToolmakerSteve
@ToolmakerSteve, deshalb habe ich die usingDirektive in C ++ so sehr vermisst : Sie kann lokal sein .
Franklin Yu
11

Ich denke, statischer Import ist wirklich nützlich, um redundante Klassennamen zu entfernen, wenn Utils-Klassen wie Arraysund verwendet werden Assertions.

Ich weiß nicht warum, aber Ross hat den letzten Satz übersprungen, der dies in der Dokumentation erwähnt, auf die er sich bezieht .

Bei sachgemäßer Verwendung kann der statische Import Ihr Programm besser lesbar machen, indem das Boilerplate für die Wiederholung von Klassennamen entfernt wird.

Grundsätzlich aus diesem Blog kopiert: https://medium.com/alphadev- Thoughts/ static- imports-are-great-but-underused- e805ba9b279f

Also zum Beispiel:

Aussagen in Tests

Dies ist der offensichtlichste Fall, in dem wir uns meiner Meinung nach alle einig sind

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

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

Verwendet Klassen und Aufzählungen

Der Klassenname kann in vielen Fällen entfernt werden, wenn Utils-Klassen verwendet werden, um den Code leichter lesbar zu machen

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

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

Das Paket java.time enthält einige Fälle, in denen es verwendet werden sollte

// 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));

Beispiel, wann nicht zu verwenden

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

// I have no idea what this is 
of("hello world");
Softarn
quelle
10

Ich benutze es oft für Farbe.

static import java.awt.Color.*;

Es ist sehr unwahrscheinlich, dass die Farben mit etwas anderem verwechselt werden.

jjnguy
quelle
1
Dies ist einer der besten Anwendungsfälle, die ich gesehen habe und die sich von dem alten JUnit / Hamcrest / TestNG unterscheiden.
Kevinarpe
3

Ich empfehle die Verwendung des statischen Imports bei Verwendung von OpenGL mit Java. Dies ist ein Anwendungsfall, der in die Kategorie "Starke Verwendung von Konstanten aus einer Dienstprogrammklasse" fällt

Berücksichtige das

import static android.opengl.GLES20.*;

Mit dieser Option können Sie den ursprünglichen C-Code portieren und etwas Lesbares schreiben, z.

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

anstelle dieser weit verbreiteten Hässlichkeit:

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);
Feuerstein
quelle
2

Statische Importe sind die einzige „neue“ Funktion von Java, die ich aufgrund der gerade erwähnten Probleme nie verwendet habe und nicht verwenden möchte.

Bombe
quelle
Danke Bombe. Nun, ich glaube, dass es sinnvoller ist, sie zu erweitern und eine Schnittstelle zu erstellen, die nur eine Reihe statischer Endspiele enthält.
Variable
2

Ich verwende 'import static java.lang.Math. *', Wenn ich mathematischen Code von C / C ++ nach Java portiere. Die mathematischen Methoden ordnen 1 zu 1 zu und erleichtern das Differenzieren des portierten Codes ohne die Qualifikation des Klassennamens.

Fracdroid
quelle
2

Ich fand dies sehr praktisch, wenn Utility-Klassen verwendet werden.

Zum Beispiel, anstatt zu verwenden: if(CollectionUtils.isNotEmpty(col))

Ich kann stattdessen:

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

Welche IMO erhöht die Lesbarkeit des Codes, wenn ich dieses Dienstprogramm mehrmals in meinem Code verwende.

Yeikel
quelle
2

Apropos Unit-Tests: Die meisten Benutzer verwenden statische Importe für die verschiedenen statischen Methoden, die von Mocking- Frameworks bereitgestellt werden, z. B. when()oder verify().

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

Und wenn Sie die einzige Behauptung verwenden, die Sie verwenden sollten, ist assertThat()es natürlich praktisch, die erforderlichen Hamcrest-Matcher statisch zu importieren, wie in:

import static org.hamcrest.Matchers.*;
GhostCat
quelle
1

Sie sind nützlich, um die Aussprache zu reduzieren, insbesondere in Fällen, in denen viele importierte Methoden aufgerufen werden und die Unterscheidung zwischen lokalen und importierten Methoden klar ist.

Ein Beispiel: Code, der mehrere Verweise auf java.lang.Math enthält

Eine andere: Eine XML-Builder-Klasse, bei der jeder Referenz der Klassenname vorangestellt wird, wird die zu erstellende Struktur ausgeblendet

kdgregory
quelle
1

Ich denke, statische Importe sind für NLS im gettext-Stil ordentlich.

import static mypackage.TranslatorUtil._;

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

Dies markiert die Zeichenfolge als eine Zeichenfolge, die extrahiert werden muss, und bietet eine einfache und saubere Möglichkeit, die Zeichenfolge durch ihre Übersetzung zu ersetzen.

Matthias Wuttke
quelle
1

Der statische IMO-Import ist eine nette Funktion. Es ist absolut richtig, dass eine starke Abhängigkeit vom statischen Import den Code unlesbar und schwer verständlich macht, zu welcher Klasse eine statische Methode oder ein statisches Attribut gehört. Nach meiner Erfahrung wird es jedoch zu einer verwendbaren Funktion, insbesondere beim Entwerfen von UtilKlassen, die einige statische Methoden und Attribute bereitstellen. Die Mehrdeutigkeit, die bei der Bereitstellung eines statischen Imports auftritt, kann durch die Festlegung von Codestandards umgangen werden. Nach meiner Erfahrung in einem Unternehmen ist dieser Ansatz akzeptabel und macht den Code sauberer und verständlicher. Am besten füge ich das _Zeichen in statische Methoden und statische Attribute ein (irgendwie von C übernommen). Anscheinend verstößt dieser Ansatz gegen die Namensstandards von Java, bietet jedoch Klarheit für den Code. Wenn wir beispielsweise eine AngleUtils-Klasse haben:

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){

    }
}

In diesem Fall sorgt der statische Import für Klarheit und die Codestruktur erscheint mir eleganter:

import static AngleUtils.*;

public class TestClass{

    public void testAngles(){

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

Sofort kann jemand erkennen, welche Methode oder welches Attribut aus einem statischen Import stammt, und die Informationen der Klasse, zu der sie gehört, werden ausgeblendet. Ich empfehle nicht, den statischen Import für Klassen zu verwenden, die integraler Bestandteil eines Moduls sind und statische und nicht statische Methoden bereitstellen, da in diesem Fall wichtig ist, zu wissen, welche Klasse bestimmte statische Funktionen bietet.

Eldjon
quelle
Vielen Dank für den Vorschlag zur Umbenennung. Übrigens wird in einigen Umgebungen traditionell ein Unterstrich verwendet, um private Methoden / Felder zu benennen. Ich erwäge eine modifizierte Konvention, z. B. H_für Importe aus einer HelperDienstprogrammklasse, die ich habe, oder C_für Commonoder U_für Utility. Alternativ habe ich erwogen, ein oder zwei Zeichenklassennamen für diese weit verbreiteten Klassen zu verwenden, war jedoch besorgt, dass diese manchmal mit lokalen Namen in Konflikt stehen könnten - haben Sie einen alten Code mit Methodennamen in Großbuchstaben.
ToolmakerSteve
-1

Sie müssen sie verwenden, wenn:

  • Sie möchten eine switchAnweisung mit Enum-Werten verwenden
  • Sie möchten, dass Ihr Code schwer verständlich wird
davetron5000
quelle
9
Das ist nicht wahr. (1) Sie können Enum-Konstanten sehr gut verwenden, ohne sie statisch zu importieren. (2) Statische Importe von beispielsweise JUnit Assert-Klassenmethoden sind klar. "assertTrue (...)" ist genauso lesbar wie "Assert.assertTrue (...)", vielleicht mehr.
Alan Krueger
5
Wenn Sie 5 statische Importe in einer 500-Zeilen-Klasse haben, ist es sehr schwer zu sagen, woher die Methoden stammen.
davetron5000
4
+1 für den Fall, dass Sie Ihren Code schwer verständlich machen möchten :)
Elende Variable
-5

Ich benutze sie, wann immer ich kann. Ich habe IntelliJ eingerichtet, um mich daran zu erinnern, wenn ich es vergesse. Ich denke, es sieht viel sauberer aus als ein voll qualifizierter Paketname.

Javamann
quelle
13
Sie denken an regelmäßige Importe. Mit statischen Importen können Sie auf Mitglieder einer Klasse verweisen, ohne sie mit einem Klassennamen zu qualifizieren, z. B. statischer Import java.lang.system.out; out.println ("foo"); // anstelle von System.out.println ("foo");
sk.
Dies ist eine sehr gute Erklärung für statische Importe ... schade, dass ich keinen Kommentar +1
abgeben