Java

基本型配列を引数にするときにNumberクラスを使うと型ごとにオーバーロードしなくて良くなるかも

More than 1 year has passed since last update.

概要: 基本型配列を引数にしてかつ様々な型に対応させたい

あまり需要ないかもしれませんが書いておきます。

Javaのプログラムで、あるメソッドの引数を数値基本型の配列で定義するとします。
ただし処理の内容は型によらず同じであり(例えば以下の様な文字列結合)、かつ実際には各要素にintが来るのかdoubleが来るのかわからない場合、果たしてどのようにメソッドを定義すればよいでしょうか。

例えば型に合わせてオーバーロード(多重定義)することにより対応させられるかもしれません。

OverloadSample.java
/*
* 以下の3つのメソッドはこのままだと1つにはまとめられない
*/
// int型配列用
public void concatArray(int[] num) {
    String line = "";
    for(int i : num) line += i + ",";
    System.out.println(line.substring(0, line.length() - 1));
}

// double型配列用
public void concatArray(double[] num) {
    String line = "";
    for(double d : num) line += d + ",";
    System.out.println(line.substring(0, line.length() - 1));
}

// float型配列用
public void concatArray(float[] num) {
    String line = "";
    for(float f : num) line += f + ",";
    System.out.println(line.substring(0, line.length() - 1));
}

しかしこれを見て、『基本型に合わせてわざわざ多重定義する必要はない!!』と思った方はお気づきかもしれませんがNumberにBoxingしてしまうという方法があります。

NumberIntegerDoubleの親クラスで、型が不明な数値配列もまとめて扱うことができます。

NumberSample.java
private String concatArray(Number[] numbers) {
    String line = "";
    for (Number number : numbers)
        line += number.toString() + ",";
    System.out.println(line.substring(0, line.length() - 1));
}

さらに数値以外の引数が来るなら、より抽象度を上げてObject配列を引数にする必要もあるでしょうが、数字だけであればNumberで十分かと思われます。また元の数値に戻すメソッド(Number.intValue(), Number.doubleValue())もあります。

ただしNumberを直接newする時は、抽象メソッドであるNumber.intValue()Number.doubleValue()の実装が必要です。

NewNumber.java
Number number = new Number() {
    @Override
    public int intValue() {
         return 0;
    }
    ...
};

余談: そもそもJavaの配列は特殊なクラス

そもそものきっかけは、配列のクラス名が分かれば、共通の親クラスを引数にするメソッドを書くことで、上記の要件を達成できるのではないのかと考えたことでした。

Javaの配列はObject継承であり、ゆえにObjectから継承されたメソッド群も使えます。しかし次のコードは何を出力するでしょうか?

ArrayClassName.java
public class ArrayClassName() {
  public static void main(String args[]) {
    boolean[] b = { true, false };
    int[] i = { 1, 2, 3 };
    double[] d = { 1.11, 2.22, 3.33 };

    System.out.println(b.getClass());
    System.out.println(i.getClass());
    System.out.println(d.getClass());
  }
}

答えはこうです

class [Z
class [I
class [D

"Array"などと表示してくれるかと思いきや、何やら意味不明な文字列が出力されます。

実はJavaの配列はObject継承でありながら、どのクラスやパッケージにも属さない特殊なクラスだったのです!
(そんなこと大学の先生は一言も教えてくれなかったよ!!)
なんか言葉だけだと矛盾してしまうようですが本当です。

ではこの意味不明な文字列は何かというと、Javaの言語仕様に

where the string "[I" is the run-time type signature for the Class object "array with component type int".

とあります。
意訳すると『"[I"は『int型配列』のクラスオブジェクトの実行時型を表すシグネチャである』となります。
シグネチャはJavaの内部処理的に振らているメソッドの識別コードのようなものですが、それがなぜ配列にフラレているのかまではわかりませんでした。

結局のところ、Javaで厳密に配列がどのような位置づけなのかはわかりませんでしたが、クラスではないObjectというのが今の結論でしょうか。

たぶん間違っていると思いますので、ネットで資料が出てこないので誰か教えて。

参考