web-dev-qa-db-ja.com

クラスを増分する方法整数参照Java別のメソッドからの値

package myintergertest;

/**
 *
 * @author Engineering
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        //this one does not increment 
        Integer n = new Integer(0);
        System.out.println("n=" + n);
        Increment(n);
        System.out.println("n=" + n);
        Increment(n);
        System.out.println("n=" + n);
        Increment(n);
        System.out.println("n=" + n);
        Increment(n);

        //this one will increment
        MyIntegerObj myInt = new MyIntegerObj(1);
        Increment(myInt);
        System.out.println("myint = " + myInt.get());
        Increment(myInt);
        System.out.println("myint = " + myInt.get());
        Increment(myInt);
        System.out.println("myint = " + myInt.get());

    }

    public static void Increment(Integer n) {
        //NO.  this doesn't work because a new reference is being assigned
        //and references are passed by value in Java
        n++;
    }

    public static void Increment(MyIntegerObj n) {
        //this works because we're still operating on the same object
        //no new reference was assigned to n here.
        n.plusplus();   //I didn't know how to implement a ++ operator...
    }
}

これらすべての結果はn = 0です。整数nはオブジェクトであり、したがって参照によって渡されるので、呼び出し元のメソッド(メイン)に増分が反​​映されないのはなぜですか?出力はn = 0 n = 1 n = 2などになると予想しました...

更新:上記のコード例を更新したことに注意してください。私が正しく理解している場合、Jon SkeetはmyIntが増加する理由とnが増加しない理由の質問に答えました。 nがIncrementメソッドで割り当てられた新しい参照を取得しているためです。しかし、myIntはメンバー関数を呼び出しているため、新しい参照は割り当てられません。

それは私が正しく理解しているように聞こえますか?

26
cchampion

いいえ、オブジェクトは参照渡しされません。参照は値で渡されます-大きな違いがあります。 Integerは不変型であるため、メソッド内で値を変更することはできません。

_n++;_ステートメントは効果的に

_n = Integer.valueOf(n.intValue() + 1);
_

そのため、nの変数Incrementに別の値を割り当てますが、Java onlyには値渡しがあります、呼び出しメソッドのnの値には影響しません。

編集:あなたの更新に答えるために:そうです。おそらく、「MyIntegerObj」型は可変であり、plusplus()を呼び出すとその内部状態が変更されます。ああ、演算子を実装する方法を気にしないでください-Javaはユーザー定義演算子をサポートしていません。

37
Jon Skeet

Java.util.concurrent.atomicからAtomicIntegerを使用できます。ショーケースに適した「incrementAndGet」メソッドがあります。

24
DerMike

C++の参照またはC#の出力パラメーターに似たものを探しています。 Java(および他の多くの言語)はこれをサポートしていません。

このタイプの動作は通常、メソッドをvoidから(この場合)intに変更することで実現されます:public static int increment(int n) { return ++n; }

メソッドが既に何かを返している場合は、2つのフィールド(パブリックにすることもできます)を持つ新しいクラスを作成できます。元の戻り値とnの新しい値です。

または、整数を汎用Cellクラス内にラップすることもできます。このCellクラスを作成する必要があるのは1回だけです。

public class Cell<T> {
  public Cell(T t) { value = t; }
  public void set(T t) { value = t; }
  public T get() { return value; }
}

その後、プリミティブを変更するメソッドが必要なすべての状況で使用できます。

public static void increment(Cell<Integer> n) {
  n.set(n.get()++);
}

Cell<Integer> n = new Cell<Integer>(0);
increment(n);
increment(n);
increment(n);
System.out.println(n.get()); // Output: 3
6
Itay Maman

サイドノート:私の謙虚な意見では、整数オブジェクトにn ++を書くことは非常に誤解を招きます。これは、Java「オートボクシング」と呼ばれる機能に依存し、プリミティブを対応するボクシングオブジェクト(intからIntegerまたはbooleanからBooleanへ)を自動的に、警告なしに変換します。率直に言って悪い機能です。はい、時には便利な変換を自動的に行うことであなたの人生をより簡単にしますが、意図しない副作用で、あなたが意図せず、実現していない変換を行うこともできます。

とにかく、あなたが明らかにしようとしていることを達成するための2つの方法があります:

1つ:インクリメント関数に、更新された値を持つ新しい整数を返すようにします。お気に入り:

   public Integer increment(Integer n)
    {
      Integer nPlus=Integer.valueOf(n.intValue()+1);
    }

次にそれを呼び出します:

Integer n=Integer.valueOf(0);
n=increment(n); // now n==1
n=increment(n); // now n==2

等。

2:整数に似ているが、変更可能な独自のクラスを作成します。お気に入り:

class MutableInteger
{
  int value;
  public MutableInteger(int n)
  {
    value=n;
  }
  public void increment()
  {
    ++value;
  }
  ... whatever other functions you need ...
}

もちろん、これの大きな問題は、Integerクラスを再発明する必要があることです。

3
Jay

Jon Skeetが述べたように、Javaのすべてのメソッドは、参照渡しではなく値渡しです。参照型は値渡しされるため、関数本体内にあるのはコピーです参照の-このコピーと元の参照は両方とも同じ値を指します。

ただし、関数本体内でコピーを再割り当てしても、関数の終了時にそのコピーはスコープ外になるため、プログラムの残りの部分には影響しません。

しかし、やりたいことをするために参照渡しを実際に必要としないことを考慮してください。たとえば、Integerをインクリメントするメソッドは必要ありません-コード内でインクリメントする必要があるポイントでインクリメントすることができます。

オブジェクトに何らかのプロパティを設定するなど、より複雑なタイプの場合、使用しているオブジェクトは変更可能です。その場合、参照のコピーは問題なく機能します。これは、自動参照解除によって、呼び出し元のセッターを持つ元のオブジェクトが表示されるためです。

3
danben

できません。 Javaは厳密に値渡しです。

http://javadude.com/articles/passbyvalue.htm を参照してください

選択肢は、整数をオブジェクト(または配列)にラップし、それを渡して更新し、値を返すか、整数をクラス/インスタンス変数にすることです。

どちらが良いかは状況次第です。

2
Joe

DerMikeで述べたように、次のようにしてこれを実現できます。

public void doStuff() {
  AtomicInteger i = new AtomicInteger(0);
  increment(i);
  System.out.println(i);
 }

  public void increment(AtomicInteger i) {
    i.set(i.get() + 1);
 }
2
JareQ

AtomicIntegerを参照してください: http://docs.Oracle.com/javase/7/docs/api/Java/util/concurrent/atomic/AtomicInteger.html

このJava.util.concurrentクラスを使用して、スレッドセーフにすることができます。

1
cjaniake

Integer値オブジェクト です。つまり、値は変更できません。

n++n = n + 1と同等であることに注意してください。 n自体の値ではなく、ローカル変数nによって参照される値を変更しているだけです。

1
Kevin
public class passByReferenceExample {
    static void incrementIt(Long b[]){
        Long c = b[0];
        c=c+1;
        b[0]=c;

    }
    public static void main(String[] args) {
        Long a[]={1L};  

        System.out.println("Before calling IncrementIt() : " + a[0]);
        System.out.println();
        incrementIt(a);
        System.out.println("after first call to incrementIt() : " + a[0]);
        incrementIt(a);
        System.out.println("after second call to incrementIt(): "+ a[0]);
        incrementIt(a);
        System.out.println("after third call to incrementIt() : "+ a[0]);
        incrementIt(a);
        System.out.println("after fourth  call to incrementIt(): "+ a[0]);    
    }
}

出力:

Before calling IncrementIt() : 1

after first call to incrementIt() : 2
after second call to incrementIt(): 3
after third call to incrementIt() : 4
after fourth  call to incrementIt(): 5
0
Suresh