Go to Contents Go to Java Page
J2SE 1.5 虎の穴
 
 

Static Import

 
 
Tiger 定数は面倒くさい
 
 

定数って面倒くさくないですか。いちいちクラス名までつけなくてはいけないんですから。

例えば、Swing のプログラムだと

        JFrame frame = new JFrame("Sample");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(button, BorderLayout.SOUTH);		
	
        button.setForeground(Color.CYAN);
        button.setBackground(Color.WHITE);
 
             ...

JFrame.EXIT_ON_CLOSE も BorderLayout.SOUTH も Color.CYAN も Color.WHITE も全部定数です。わざわざクラス名をつける必要があるのはご存知のとおりです。

名前が長いクラスなんかだと本当に頭にきませんか ? java.awt.KeyboardFocusManager クラスの BACKWARD_TRAVERSAL_KEYS 定数なんて最低ですね ^^;;

定数だけ定義してあるインタフェース (例えば javax.swing.SwingConstants インタフェース) を使って、意味もなくインプリメントするという方法もあります。しかし、この方法はインタフェースの本来の目的からは外れていますし、Joshua Bloch も Effective Java の中でこの方法を批判しています。

Static インポートを使えばこの面倒さを少しだけ解消してくれます。

 

 
 
Tiger 定数をインポートしてしまおう
 
 

Static インポートは定数もクラスと同じように import 文で記述できるようにしようということです。

簡単な例を示しましょう。たとえば、先ほどの Swing の例は

import static java.awt.BorderLayout.SOUTH;
import static java.awt.Color.CYAN;
import static javax.swing.JFrame.EXIT_ON_CLOSE;

と普通の import 文と並べて記述すれば

        JFrame frame = new JFrame("Sample");
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.getContentPane().add(button, SOUTH);		
	
        button.setForeground(CYAN);
 
             ...

というようにクラス名を記述せずに、定数名だけで使用することができます。

Static インポートは正確には次のように記述します。

    import static TypeName.Identifier;

もしくは

    import static TypeName.*;

のようにアスタリスクを使用することもできます。

先ほどの例では

    import static java.awt.Color.CYAN;
    import static java.awt.Color.WHITE;

と書くこともできますが、次のように書くこともできます。

    import static java.awt.Color.*;

これだと Color クラスで定義されているすべての定数をインポートします。

もちろん、インポートできるのは static で定義してあるクラス変数です。

 

 
 
Tiger 定数だけではないんです
 
 

Static インポートというと定数だけインポートできるような感じを受けますが、実をいうと違います。

なんと、satic なクラスメソッドもインポートできます。たとえば、次のようなコードが書けてしまいます。

import static java.lang.Math.sin;
import static java.lang.Math.PI;
 
public class MethodImportTest1 {
    public static void main(String[] args) {
        System.out.println("sin(PI) = " + sin(PI));
    }
}

sin メソッドは Math クラスの static なクラスメソッドです。Static インポートすると、このメソッドもクラス名をつけずにコールすることができるようになります。さらにアスタリスクを使えば、一緒にインポートすることも可能です。

import static java.lang.Math.*;
 
public class MethodImportTest2 {
    public static void main(String[] args) {
        System.out.println("sin(PI) = " + sin(PI));
    }
}

ただ、私はあまりアスタリスクを使うことはお勧めしません。というか、普通の import 文でもアスタリスクは使わないほうがいいと思っています。

これはプログラムの保守性に関わってくるのです。プログラムを作者でない第 3 者が見ているとき、見知らぬクラスが出てきたら、これがコアライブラリのクラスか自作したクラスかは import 文を見ることで分かります。アスタリスクが使われていたら、そのクラスがどのパッケージに属しているかが分からなくなってしまいます。

人にやさしいプログラミングが私のモットーなんです。

さて、それはさておき、クラスメソッド以外にもインポートできるものがあります。それは JSR-201 で一緒に導入された Enum です。

例を示しましょう。とても単純に Enum だけを定義したクラスを作りました。

package abc;
 
public class Card {
    public enum Suit {SPADE, DIAMOND, HEART, CLUB}
}

これをインポートしてみましょう。Enum をインポートするときは Enum の型名まで記述する必要があります。

import static abc.Card.Suit.SPADE;
 
public class EnumImportTest1 {
    public static void main(String[] args) {
        System.out.println(SPADE);
    }
}

もちろん次のようにアスタリスクを使うこともできます。

    import static abc.Card.Suit.*;

 
 
Tiger 気をつけなくてはいけないこと
 
 

自分で定数を含んだクラスを作って、他のクラスでそれをインポートするときに気をつけなくてはいけないことがあります。

それは定数を定義したクラスは必ずパッケージに属していなくてはいけないということです。

先ほどの Card クラスは abc というパッケージでした。これをパッケージなしにしてみましょう。Card.java を次の EnumImportTest2.java と同じディレクトにおき、一番上の package 文を削除します。後はこれを呼び出すプログラムを作ります。

import static Card.Suit.SPADE;
 
public class EnumImportTest2 {
    public static void main(String[] args) {
        System.out.println(SPADE);
    }
}

さて、これをコンパイルしてみましょう。

C:\JSR201\examples>javac EnumImportTest2.java
EnumImportTest2.java:1: パッケージ Card は存在しません。
import static Card.Suit.SPADE;
                   ^
EnumImportTest2.java:1: static import only from classes and interfaces
import static Card.Suit.SPADE;
^
EnumImportTest2.java:5: シンボルを見つけられません。
シンボル: 変数 SPADE
場所    : EnumImportTest2 の クラス
        System.out.println(SPADE);
                           ^
EnumImportTest2.java:5: 内部エラーです。println(boolean) を java.io.PrintStream
で (SPADE) にインスタンス生成できません。
        System.out.println(SPADE);
                  ^
エラー 4 個
 
C:\JSR201\examples>

Card がパッケージとして認識されてしまっています。

これはもしかしたら、バグなのかもしれません。でも、なるべくパッケージはつけた方がいいですね。

 

 
 
Tiger 実際はどうなってんだ ????
 
 

とはいうもののやっぱり気になる、裏側が。

そこでいつものように、逆コンパイラの Jad を使って調べてみました。

逆コンパイルしたのは MethodImportTest1 です。

Jad の結果は次のようになりました。

import java.io.PrintStream;
 
public class MethodImportTest1
{
 
    public MethodImportTest1()
    {
    }
 
    public static void main(String args[])
    {
        System.out.println("sin(PI) = " + Math.sin(3.1415926535897931D));
    }
}

定数は展開されていて、static なクラスメソッドはちゃんと頭にクラス名が補完されていました。

同じように EnumImportTest1 も逆コンパイルしてみると

import abc.Card;
import java.io.PrintStream;
 
public class EnumImportTest1
{
 
    public EnumImportTest1()
    {
    }
 
    public static void main(String args[])
    {
        System.out.println(abc.Card.Suit.SPADE);
    }
}

Enum もクラスメソッドと同じようにクラス名が補完されていて、Enum の型名も補完されているようです。

 

 
 
Tiger おわりに
 
 

Static インポートはなかなか便利です。

Static インポートを使っても、コンパイルすれば従来の方式に直してくれるだけなので、パフォーマンスの劣化などを心配せずにすみます。

これこそ Ease of Development ですね。

 

今回使用したサンプルはここからダウンロードできます。

参考

(Oct. 2003)
(改訂 Jan. 2004)

 
 
Go to Contents Go to Java Page