web-dev-qa-db-ja.com

整数クラスを参照で正しく渡すにはどうすればよいですか?

誰かが私のためにここで何が起こっているのかを明確にできることを望んでいます。整数クラスを少し掘り下げましたが、整数はオーバーライド+演算子何が間違っているのかわかりませんでした。私の問題はこの行にあります:

Integer i = 0;
i = i + 1;  // ← I think that this is somehow creating a new object!

私の推論は次のとおりです:Javaは値渡し( または参照の値渡し )であるため、次の例では整数オブジェクトは毎回増加します。

public class PassByReference {

    public static Integer inc(Integer i) {
        i = i+1;    // I think that this must be **sneakally** creating a new integer...  
        System.out.println("Inc: "+i);
        return i;
    }

    public static void main(String[] args) {
        Integer integer = new Integer(0);
        for (int i =0; i<10; i++){
            inc(integer);
            System.out.println("main: "+integer);
        }
    }
}

これは私の予想出力です:

 Inc:1 
 main:1 
 Inc:2 
 main:2 
 Inc:3 
 main:3 
 Inc:4 
 main:4 
 Inc:5 
 main:5 
 Inc:6 
 main:6 
 ... 

これは実際の出力です。

 Inc:1 
 main:0 
 Inc:1 
 main:0 
 Inc:1 
 main:0 
 ... 

なぜこのように振る舞うのですか?

54

2つの問題があります。

  1. 整数は参照ではなく値で渡されます。メソッド内の参照を変更しても、呼び出し元のメソッドで渡された参照には反映されません。
  2. 整数は不変です。 Integer#set(i)のようなメソッドはありません。それ以外の場合は、単にそれを利用することができます。

動作させるには、inc()メソッドの戻り値を再割り当てする必要があります。

integer = inc(integer);

値渡しについてもう少し学ぶために、別の例を示します。

public static void main(String... args) {
    String[] strings = new String[] { "foo", "bar" };
    changeReference(strings);
    System.out.println(Arrays.toString(strings)); // still [foo, bar]
    changeValue(strings);
    System.out.println(Arrays.toString(strings)); // [foo, foo]
}
public static void changeReference(String[] strings) {
    strings = new String[] { "foo", "foo" };
}
public static void changeValue(String[] strings) {
    strings[1] = "foo";
}
55
BalusC

整数は不変です。カスタムラッパークラスでintをラップできます。

class WrapInt{
    int value;
}

WrapInt theInt = new WrapInt();

inc(theInt);
System.out.println("main: "+theInt.value);
18
Markos

参照渡しする方法は2つあります

  1. Apache Commonsライブラリの org.Apache.commons.lang.mutable.MutableInt を使用します。
  2. 以下に示すようにカスタムクラスを作成します

これを行うサンプルコードを次に示します。

public class Test {
    public static void main(String args[]) {
        Integer a = new Integer(1);
        Integer b = a;
        Test.modify(a);
        System.out.println(a);
        System.out.println(b);

        IntegerObj ao = new IntegerObj(1);
        IntegerObj bo = ao;
        Test.modify(ao);
        System.out.println(ao.value);
        System.out.println(bo.value);
    }


    static void modify(Integer x) {
        x=7;
    }
    static void modify(IntegerObj x) {
        x.value=7;
    }   
}

class IntegerObj {
    int value;
    IntegerObj(int val) {
        this.value = val;
    }
}

出力:

1
1
7
7
15
Rishi Dua

OPからの実際の質問を説明する上記の良い回答。

グローバルに更新する必要がある番号を誰かに渡す必要がある場合は、AtomicInteger()提案されたさまざまなラッパークラスを作成する代わりに、またはサードパーティのライブラリに依存します。

AtomicInteger()もちろんスレッドセーフアクセスに主に使用されますが、パフォーマンスヒットが問題にならない場合は、この組み込みクラスを使用してください。もちろん、追加されたボーナスは明らかなスレッドセーフです。

import Java.util.concurrent.atomic.AtomicInteger
14
Hans

ここに表示されているのは、オーバーロード+ oparator、ただしオートボクシングの動作。 Integerクラスは不変であり、コードは次のとおりです。

Integer i = 0;
i = i + 1;  

(オートボクシング後)コンパイラーは次のように見えます:

Integer i = Integer.valueOf(0);
i = Integer.valueOf(i.intValue() + 1);  

したがって、Integerインスタンスが変更されたという結論は正しいが、こっそりではない-これはJava言語定義:-)と一致している

12
rsp

あなたはここに正しいです:

_Integer i = 0;
i = i + 1;  // <- I think that this is somehow creating a new object!
_

最初:整数は不変です。

2番目:整数クラスは_+_演算子をオーバーライドしていません。その行でオートボックス化とオートボックス化が関係しています(Javaの古いバージョンでは上記の行でエラーが発生します) 。
_i + 1_と記述すると、コンパイラは最初に整数を(プリミティブ)intに変換して、追加を実行します:自動ボックス化。次に、_i = <some int>_を実行すると、コンパイラはintから(新しい)整数に変換します:オートボクシング。
したがって、_+_は実際にプリミティブintsに適用されます。

6

私はそれがあなたを捨てているオートボクシングだと思います。

コードのこの部分:

   public static Integer inc(Integer i) {
        i = i+1;    // I think that this must be **sneakally** creating a new integer...  
        System.out.println("Inc: "+i);
        return i;
    }

実際には、次のようなコードに要約されます。

  public static Integer inc(Integer i) {
        i = new Integer(i) + new Integer(1);      
        System.out.println("Inc: "+i);
        return i;
    }

もちろん、渡される参照は変更されません。

このようなもので修正できます

  public static void main(String[] args) {
        Integer integer = new Integer(0);
        for (int i =0; i<10; i++){
            integer = inc(integer);
            System.out.println("main: "+integer);
        }
    }
2
bwawok

Inc()関数をこれに変更した場合

 public static Integer inc(Integer i) {
      Integer iParam = i;
      i = i+1;    // I think that this must be **sneakally** creating a new integer...  
      System.out.println(i == iParam);
      return i;
  }

常に「false」と出力されることがわかります。つまり、追加によってIntegerの新しいインスタンスが作成され、それがlocal変数i(iは実際に渡された参照のコピーであるため「ローカル」)に格納され、呼び出しメソッドはそのまま。

整数は不変のクラスです。つまり、整数の値は変更できませんが、新しいインスタンスを取得する必要があります。この場合、次のように手動で行う必要はありません。

i = new Integer(i+1); //actually, you would use Integer.valueOf(i.intValue()+1);

代わりに、オートボクシングによって行われます。

2
f1sh

1)参照のコピーのみが値として仮パラメーターに送信されます。仮パラメータ変数に他の値が割り当てられると、仮パラメータの参照は変更されますが、実際のパラメータの参照はこの整数オブジェクトの場合と同じままです。

パブリッククラスUnderstandingObjects {

public static void main(String[] args) {

    Integer actualParam = new Integer(10);

    changeValue(actualParam);

    System.out.println("Output " + actualParam); // o/p =10

    IntObj obj = new IntObj();

    obj.setVal(20);

    changeValue(obj);

    System.out.println(obj.a); // o/p =200

}

private static void changeValue(Integer formalParam) {

    formalParam = 100;

    // Only the copy of reference is set to the formal parameter
    // this is something like => Integer formalParam =new Integer(100);
    // Here we are changing the reference of formalParam itself not just the
    // reference value

}

private static void changeValue(IntObj obj) {
    obj.setVal(200);

    /*
     * obj = new IntObj(); obj.setVal(200);
     */
    // Here we are not changing the reference of obj. we are just changing the
    // reference obj's value

    // we are not doing obj = new IntObj() ; obj.setValue(200); which has happend
    // with the Integer

}

}

クラスIntObj {整数a;

public void setVal(int a) {
    this.a = a;
}

}

0
awinas kannan