web-dev-qa-db-ja.com

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

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

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

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

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

それでは、doesの場合、静的インポートメソッドに意味がありますか?いつやったの?修飾されていない呼び出しの外観が好きでしたか/好きでしたか?

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

ご回答ありがとうございます。質問を終了します。

125

これは、彼らがこの機能をリリースしたときのサンのガイドによるものです(オリジナルを強調)。

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

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

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

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

個人的には、この言語機能veryを使用することはめったになく、ほとんどの場合は定数または列挙型でのみ使用し、メソッドでは使用しません。私にとっては、トレードオフはほとんど価値がありません。

137
Ross

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

// old way
import junit.framework.TestCase;

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

JUnit 4では、テストクラスはTestCaseを拡張する必要がなくなり、代わりにアノテーションを使用できます。その後、org.junit.Assertから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 この方法で使用します。

61
Rob Hruska

効果的な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を使用すると、定義に移動できるため、これはそれほど問題ではありません。

インポートしたファイルの内容を何度も何度も使用していることに気付いた場合にのみ、これを控えめに使用する必要があります。

編集:この質問が参照しているものであるため、メソッドに固有になるように更新されました。この原則は、インポートされるもの(定数またはメソッド)に関係なく適用されます。

25
Rob Hruska

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

11
Joel

私は色のためにそれをたくさん使用します。

static import Java.awt.Color.*;

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

10
jjnguy

静的インポートは、ArraysAssertionsなどのutilsクラスを使用するときに、冗長なクラス名を削除するのに本当に役立つと思います。

なぜかはわかりませんが、ロスは 彼が参照しているドキュメント でこれについて言及している最後の文をスキップしました。

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

このブログから基本的にコピー: 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");
7
softarn

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

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

そしてもちろん、唯一のアサートを使用する場合は、 `assertThat()を使用する必要があります。次のように、必要なハムクレストマッチャーを静的にインポートすると便利です。

import static org.hamcrest.Matchers.*;
2
GhostCat

JavaでOpenGLを使用する場合は、static importの使用をお勧めします。これは、「ユーティリティクラスからの定数の頻繁な使用」に該当するユースケースです カテゴリー

それを考慮してください

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
Flint

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

2
Bombe

C/C++からJavaに数学の重いコードを移植するときは、「import static Java.lang.Math。*」を使用します。数学メソッドは1から1にマッピングされ、クラス名の修飾なしで移植されたコードの差分を簡単にします。

2
Fracdroid

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

import static mypackage.TranslatorUtil._;

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

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

1
Matthias Wuttke

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

たとえば、使用する代わりに:if(CollectionUtils.isNotEmpty(col))

代わりにできます:

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

コードでこのユーティリティを複数回使用すると、どのIMOがコードの可読性を高めます。

1
Yeikel

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

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

1
eldjon

特に、呼び出されるインポートされたメソッドが多数あり、ローカルメソッドとインポートされたメソッドの区別が明確な場合、冗長性を減らすのに役立ちます。

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

別の例: XMLビルダークラス ここで、すべての参照にクラス名を追加すると、構築中の構造が非表示になります

1
kdgregory

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

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