2012/10/13

Javaがよくわからん・・・

AndroidでNFC-Fのコマンド送受信をするときは、NfcF.transceive()を使う。
こんなメソッドだ。

public byte[] transceive(byte[] data) throws IOException {
    return transceive(data, true);
}

戻ってくるのは、応答した結果のbyte[]。

さて、このbyte[]、どうしたらいいんだろうか?
どっちかだと思う。

  • transceive()以下でメモリ確保してあげてるから、そのまま使っていいよ。
  • 暫定でメモリを渡してるんだから、さっさと自分でコピーしてね。

悩んでいたが、文字にしてみると、前者だろう、ということに気付いた。

 

まだ言語仕様がよくわかっていないが、Javaの引数はCの引数と同じイメージでいいようだ。

void f() {
  byte[] a = new byte[] { 1, 2, 3 };  //「new byte[]」はいらない?
  g(a);
  h(a);
}

void g(byte[] a) {
  a[0] = 4;
}

void h(byte[] b) {
  b = new byte[] { 5, 6, 7 };
}

f()が持つa[]は、g()によって書き換えはされるが、h()によって置き換えられることはない。
だから、相手先にメモリを確保してもらうなら、戻り値でもらうようにするのがよさそうだ。
引数で返してもらうなら、配列にしてしまうしかないのかな?

void f() {
  byte[][] a = new byte[1][];
  a[0] = new byte[] { 1, 2, 3 };
  h(a);
}

void h(byte[][] b) {
  b[0] = new byte[] { 5, 6, 7 };
}

 

なんか、C#の文法とかが入り交じってしまって、コンパイルもしてないから、私が思っているイメージが伝わればいいかな、ということで。


そしてまだよくわかってないものに、System.arraycopy()が挙げられる。

System.arraycopy()は、シャローコピー(shallow copy)だそうだ。
って、みんないってるけど、言語仕様なんだろうか?

package test;
 
public class Test {
    public static void main(String[] args) {
 
        byte[] a = { 1, 2, 3, 4, 5 };
        byte[] b = { 6, 7, 8, 9, 0 };
        byte[] c = new byte[2];
        System.arraycopy(a, 0, c, 0, 1);
        System.arraycopy(b, 0, c, 1, 1);
 
        //最初の値
        System.out.print("a:");
        for(byte k : a) {
            System.out.print(k + " ");
        }
        System.out.println();
        System.out.print("b:");
        for(byte k : b) {
            System.out.print(k + " ");
        }
        System.out.println();
        System.out.print("c:");
        for(byte k : c) {
            System.out.print(k + " ");
        }
        System.out.println();
        
        //書き換え
        c[0] = 10;
        c[1] = 11;
        
        //最後の値
        System.out.println("----------");
 
        System.out.print("a:");
        for(byte k : a) {
            System.out.print(k + " ");
        }
        System.out.println();
        System.out.print("b:");
        for(byte k : b) {
            System.out.print(k + " ");
        }
        System.out.println();
        System.out.print("c:");
        for(byte k : c) {
            System.out.print(k + " ");
        }
        System.out.println();
    }
}

結果

a:1 2 3 4 5
b:6 7 8 9 0
c:1 6
----------
a:1 2 3 4 5
b:6 7 8 9 0
c:10 11

 

あれ?
参照してるんだから、参照先が変わると参照元も変わるんじゃないの??

じゃあ、参照元だけ変更して、参照先はそのままにしてみた。

 

結果

a:1 2 3 4 5
b:6 7 8 9 0
c:1 6
----------
a:10 2 3 4 5
b:11 7 8 9 0
c:1 6

 

変わらん・・・。
ちゃんと値コピーされている。

 

いや、不思議に思ったのだ。
コピーって配列の一部だけコピーすることができるけど、それをわざわざ「ここからここまではこのオブジェクトを参照」「ここからここまでは、あのオブジェクト参照」なんて管理するのは、非常に面倒だし、かえって遅くなるではないか。

 

System.arraycopy()の「shallow」な対象は、配列オブジェクトではなく、配列オブジェクトの要素部分ということなんだな。
上の例では、要素部分はプリミティブなbyte型だから、そのまま値コピーされている。
要素部分がStringみたいな型だったら、それはアドレスをコピーするだけにしていて、その先は同じものを参照しますよ、ということか。

 

 image
要素部分がオブジェクト型の場合は、アドレスだけをコピー

 

image
要素部分がプリミティブ型の場合は、アドレスに相当するのが値なので、値コピー

 

試してよかった・・・。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。

注: コメントを投稿できるのは、このブログのメンバーだけです。