メソッドの静的インポートの良いユースケースは何ですか?


137

メソッドの静的インポートは良いアイデアではなかったというレビューコメントを受け取りました。静的インポートは、ほとんどが静的メソッドを持つDAクラスからのメソッドのインポートでした。そのため、ビジネスロジックの途中で、どうやら現在のクラスに属しているように見えるdaアクティビティがありました。

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

レビュー担当者は、私がコードを変更することには熱心ではなく、変更しませんでしたが、私は彼にある程度同意します。静的インポートではない理由の1つは、メソッドが定義された場所がわかりにくく、現在のクラスではなく、スーパークラスでもないため、定義を特定するのに時間がかかりすぎたためです(Webベースのレビューシステムにはクリック可能ではありません) IDEのようなリンク:-)これは問題だとは思いません。静的インポートはまだ非常に新しく、すぐにそれらを見つけることに慣れるでしょう。

しかし、私が同意するもう1つの理由は、修飾されていないメソッド呼び出しは現在のオブジェクトに属しているようであり、コンテキストをジャンプするべきではないということです。しかし、もしそれが本当に属していたなら、そのスーパークラスを拡張することは理にかなっています。

だから、ときそれは、静的インポート方法に意味をなさない?いつやったの?修飾されていない呼び出しがどのように見えるか、好きですか?

編集:誰もが現在のクラスのメソッドとしてそれらを混乱させるつもりがない場合、人気のある意見は静的インポートメソッドであるようです。たとえば、java.lang.Mathおよびjava.awt.Colorのメソッドの例です。しかし、absとgetAlphaがあいまいでなければ、readEmployeeがなぜなのかわかりません。多くのプログラミングの選択と同様に、これも個人的な好みの問題だと思います。

ご回答ありがとうございます。質問を締めくくります。


2
静的インポートの非常に優れた使用方法を以下
library

1
@ mr5構文はimport static、機能はstatic import
Miserable Variable

回答:


150

これは、機能がリリースされたときのSunのガイドからのものです(強調はオリジナル)。

では、静的インポートをいつ使用する必要がありますか?非常に控えめに!定数のローカルコピーを宣言したり、継承を悪用したりしたい場合(定数インターフェイスのアンチパターン)にのみ使用してください。...静的インポート機能を使いすぎると、プログラムが読みにくく、保守できなくなり、インポートするすべての静的メンバーで名前空間が汚染されます。コードの読者(あなたがコードを書いてから数か月後)は、静的メンバーがどのクラスから来ているのかを知りません。クラスからすべての静的メンバーをインポートすると、読みやすさに特に害を及ぼす可能性があります。1つまたは2つのメンバーだけが必要な場合は、個別にインポートします。

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

具体的にお話ししたい部分が2つあります。

  • 静的インポートを使用するのは、「継承を悪用する」誘惑に駆られた場合のみにしてください。この場合、あなたはBusinessObjectを使いたくありextend some.package.DAませんか?もしそうなら、静的インポートはこれを処理するよりきれいな方法かもしれません。拡張を夢見たことがない場合some.package.DA、これはおそらく静的インポートの不適切な使用法です。入力時に数文字を保存するためだけに使用しないでください。
  • 個々のメンバーをインポートします。import static some.package.DA.save代わりに言いますDA.*。これにより、このインポートされたメソッドがどこから来ているのかを簡単に見つけることができます。

個人的に、私はこの言語機能を使用することは非常にまれであり、ほとんど常に定数または列挙型でのみ使用し、メソッドでは使用しません。私にとって、トレードオフはそれだけの価値はほとんどありません。


9
同意した。静的インポートのVeeeryをときどき使用して、実際にコードの追跡を大幅に容易にしました。
Neil Coffey、

65

静的インポートのもう1つの妥当な用途はJUnit 4です。以前のバージョンのJUnitでは、assertEqualsとのようなメソッドfailが継承され、テストクラスが拡張されましたjunit.framework.TestCase

// old way
import junit.framework.TestCase;

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

JUnit 4では、テストクラスを拡張する必要がなくなり、TestCase代わりにアノテーションを使用できます。次に、以下からassertメソッドを静的にインポートできます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のドキュメント、このようにそれを使用しました。


4
同意します。テストケースの簡略化は、意図が誤解される可能性が低い1つの場所です。
ビルミシェル

6
私たちのプロジェクトにはこれがあり、実際にassert()を使用している人に問題があり、Assertパッケージの静的インポートからのものであると誤って考えていました。この問題を検出すると、コードベースのクイックスキャンにより、テストで約30のインスタンスが見つかりました。つまり、テストを実行するときにDEBUGフラグが設定されていないため、テストフレームワークが実行されたときに30のアサーションが実行されていませんでした。
Chris Williams、

27

効果的なJava、第2版アイテム19の最後にあるユーティリティクラスの定数を頻繁に使用している場合は、静的インポートを使用できると述べています。この原則は、定数とメソッドの両方の静的インポートに適用されると思います。

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

これには長所と短所があります。メソッドが定義されている場所に関するいくつかの即時情報が失われる代わりに、コードが少し読みやすくなります。ただし、優れたIDEでは定義に進むことができるため、これはそれほど問題にはなりません。

これは慎重に使用し、インポートしたファイルの内容を何度も何度も使用している場合にのみ使用してください。

編集:この質問が言及している方法であるため、メソッドにより具体的になるように更新されました。原則は、何がインポートされるか(定数またはメソッド)に関係なく適用されます。


1
私の質問は、フィールドではなく静的インポートメソッドについてです。
悲惨な変数

7
おそらくUtilityClassWithFrequentlyUsedMethods短くする必要があります。
Steve Kuo


@ Rob-Hruska頻繁に使用する予定がある場合、静的インポートメソッドまたはフィールドを新しいメソッドまたはフィールドでラップするだけではいいのではないですか?静的にインポートしないようにできますか?など:double myPI = Math.PI;そして、私は単にのmyPI代わりに参照し続けることができMath.PIます。
Abdul 14

@アブドゥル-そうだね。
Rob Hruska 14

14

これらは読みやすさの観点から問題が生じる可能性があり、慎重に使用する必要があることに同意します。ただし、一般的な静的メソッドを使用すると、読みやすさが向上します。たとえば、JUnitテストクラスでは、のようなメソッドassertEqualsはどこから来たのかが明らかです。同様にからのメソッドjava.lang.Math


5
そして、Math.round(d)とround(d)を見ることの何が悪いのか
スティーブKuo

5
@SteveKuo-数学者が式を操作するときに1文字の変数名を使用するのと同じ理由で、長い名前はステートメント全体の読みやすさを妨げる場合があります。複数の三角関数を含む式を考えます。簡単に理解できる数式:sin x cos y + cos x sin y。Javaでは次のようになりますMath.sin(x) * Math.cos(y) + Math.cos(x) * Math.sin(y)。読むのはひどい。
ToolmakerSteve

@ToolmakerSteve、それusingがC ++のディレクティブをあまり見逃した理由です。それらはローカルになる可能性があります。
フランクリンユー

11

静的インポートはArrays、やのようなutilsクラスを使用するときに冗長なクラス名を削除するのに非常に役立つと思いますAssertions

理由は不明ですが、ロスは、彼が参照しているドキュメントでこれについて言及している最後の文をスキップしました。

静的インポートを適切に使用すると、クラス名の繰り返しのボイラープレートを削除して、プログラムを読みやすくすることができます。

基本的にこのブログからコピー:https : //medium.com/alphadev-thoughts/static-imports-are-great-but-underused-e805ba9b279f

だから例えば:

テストのアサーション

これは私たち全員が同意する最も明白なケースです

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

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

Utilsクラスと列挙型

utilsクラスを使用すると、多くの場合、クラス名を削除してコードを読みやすくすることができます

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

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

java.timeパッケージには、使用する必要があるいくつかのケースがあります

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

使用しない場合の例

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

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

10

Colorによく使います。

static import java.awt.Color.*;

色が他の色と混同されることはほとんどありません。


1
これは、以前のJUnit / Hamcrest / TestNGとは異なる、私が見た中で最高のユースケースの1つです。
kevinarpe 2015年

3

JavaでOpenGLを使用する場合、静的インポートの使用をお勧めします。これは、「ユーティリティクラスの定数の多用」カテゴリに該当するユースケースです。

考えて

import static android.opengl.GLES20.*;

オリジナルのCコードを移植して、次のような読みやすいものを書くことができます。

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

その一般的な広範な醜さの代わりに:

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

2

静的インポートは、あなたが今述べた問題のために、私が使用したことがなく、使用するつもりもない、Javaの唯一の「新しい」機能です。


ボンベに感謝します。まあ、私は彼らが拡張して静的な決勝戦の束を含んでいるだけのインターフェースを作らなければならないということの方が理にかなっていると思います。
悲惨な変数

2

数学の重いコードをC / C ++からJavaに移植するときは、「import static java.lang.Math。*」を使用します。数学メソッドは1対1にマップし、クラス名の修飾なしで移植されたコードの比較を容易にします。


2

これは、ユーティリティクラスを使用するときに非常に便利であることがわかりました。

たとえば、次のように使用します。 if(CollectionUtils.isNotEmpty(col))

代わりに:

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

コードでこのユーティリティを複数回使用すると、コードの読みやすさが向上するIMOはどれですか。


2

ユニットテストについて話す:ほとんどの人は、モックフレームワークが提供するさまざまな静的メソッド(when()またはなど)に静的インポートを使用しますverify()

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

そしてもちろん、1つだけを使用assertThat()する場合は、次のように、必要なhamcrestマッチャーを静的にインポートすると便利です。

import static org.hamcrest.Matchers.*;

1

特に呼び出されるインポートされたメソッドが多く、ローカルメソッドとインポートされたメソッドの違いが明確である場合に、これらは冗長を減らすのに役立ちます。

1つの例:java.lang.Mathへの複数の参照を含むコード

もう1つ:すべての参照の前にクラス名を付加すると、構築される構造が非表示になるXMLビルダークラス


1

静的インポートは、gettextスタイルのNLSには適していると思います。

import static mypackage.TranslatorUtil._;

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

これは、文字列を抽出する必要がある文字列としてマークし、文字列をその翻訳に置き換える簡単でクリーンな方法を提供します。


1

IMO静的インポートは非​​常に優れた機能です。静的インポートに大きく依存すると、コードが読みにくくなり、静的メソッドまたは属性がどのクラスに属しているかを理解することが難しくなります。しかし、私の経験では、特にUtilいくつかの静的メソッドと属性を提供するクラスを設計するときに、それは使用可能な機能になります。静的インポートを提供するたびに生じるあいまいさは、コード標準を確立することで回避できます。企業内での私の経験では、このアプローチは許容可能であり、コードをよりクリーンで理解しやすくします。できれば、_文字を静的メソッドと静的属性の前に挿入します(どういうわけかCから採用されてい)。。どうやらこのアプローチはJavaの命名基準に違反していますが、コードを明確にしています。たとえば、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){

    }
}

この場合、静的インポートは明快さを提供し、コード構造は私にとってよりエレガントに見えます:

import static AngleUtils.*;

public class TestClass{

    public void testAngles(){

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

すぐに、どのメソッドまたは属性が静的インポートからのものであるかを誰かが知ることができ、それが属しているクラスの情報を非表示にします。モジュールの不可欠な部分であるクラスに静的インポートを使用し、静的および非静的メソッドを提供することはお勧めしません。これらの場合、特定の静的機能を提供するクラスを知ることが重要です。


ネーミングに関する提案をありがとう。ところで、前部の下線は、プライベートメソッド/フィールドに名前を付けるために一部の環境で伝統的に使用されています。私は、次のような変更大会、検討しているH_からの輸入のためにHelper私が持っているユーティリティクラス、またはC_のためにCommon、またはU_のためにUtility。または、これらの広く使用されているクラスに1つまたは2つの文字クラス名を使用することを検討しましたが、ローカル名と競合することがあるのではないかと心配しました-大文字のメソッド名を持つレガシーコードがあります。
ToolmakerSteve 2015年

-1

次の場合に使用する必要があります。

  • switch列挙値を持つステートメントを使用したい
  • コードを理解しにくくしたい

9
本当じゃない。(1)enum定数を静的にインポートすることなく完全に使用できます。(2)たとえば、JUnit Assertクラスメソッドの静的インポートは、ベルとして明確です。"assertTrue(...)"は、 "Assert.assertTrue(...)"と同じくらい読みやすいかもしれません。
アランクルーガー

5
500行のクラスに5つの静的インポートがある場合、メソッドがどこから来たかを判断するのは非常に困難です。
davetron5000 2009年

4
コードをわかりにくくしたい場合は+1 :)
Miserable Variable

-5

できる限り使用します。忘れた場合に通知するIntelliJセットアップがあります。完全修飾パッケージ名よりもずっときれいに見えます。


13
あなたは定期的な輸入を考えています。静的インポートを使用すると、クラス名で修飾せずにクラスのメンバーを参照できます。例:static import java.lang.system.out; out.println( "foo"); // System.out.println( "foo");の代わりに
sk。

これは静的インポートの非常に良い説明です...残念ながらコメントを+1できません
Eldelshell
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.