Javaでプリミティブの参照渡しを行う方法


116

このJavaコード:

public class XYZ {   
    public static void main(){  
        int toyNumber = 5;   
        XYZ temp = new XYZ();  
        temp.play(toyNumber);  
        System.out.println("Toy number in main " + toyNumber);  
    }

    void play(int toyNumber){  
        System.out.println("Toy number in play " + toyNumber);   
        toyNumber++;  
        System.out.println("Toy number in play after increement " + toyNumber);   
    }   
}  

これを出力します:

 
プレイ中のおもちゃの数5  
インクリメント後のおもちゃの数6  
メイン5のおもちゃの数  

C ++ではtoyNumber、シャドウイングを回避するために、参照渡しとして変数を渡すことができます。つまり、以下のように同じ変数のコピーを作成します。

void main(){  
    int toyNumber = 5;  
    play(toyNumber);  
    cout << "Toy number in main " << toyNumber << endl;  
}

void play(int &toyNumber){  
    cout << "Toy number in play " << toyNumber << endl;   
    toyNumber++;  
    cout << "Toy number in play after increement " << toyNumber << endl;   
} 

C ++の出力は次のようになります。

プレイ中のおもちゃの数5  
インクリメント後のおもちゃの数6  
メイン6のおもちゃの数  

私の質問は-Javaが参照渡しではなく値渡しの場合、C ++コードと同じ出力を取得するJavaの同等のコードは何ですか?


22
これは私には重複しているようには見えません-重複していると思われる質問は、Javaの仕組みとその用語の意味、つまり教育を扱っていますが、この質問は、参照渡し動作に似た何か、多くのCを取得する方法を具体的に尋ねています/ C ++ / D / Adaプログラマーは、Javaがすべて値渡しである理由を気にするのではなく、実用的な作業を行うために不思議に思うかもしれません。
DarenW 2011年

1
@DarenW私は完全に同意します-再開することに投票しました。ああ、そしてあなたは同じことをするのに十分な評判があります:-)
Duncan Jones

質問は参照値にも同様に当てはまるため、プリミティブに関する議論は誤解を招くものです。
shmosel 2016年

「C ++では、toyNumber変数を参照渡しとしてシャドウイングを回避できます」 - メソッドでtoyNumber宣言された変数がmainメソッドのスコープ内にないため、これはシャドウイングではありませんplay。C ++およびJavaでのシャドウイングは、スコープのネストがある場合にのみ発生します。en.wikipedia.org/wiki/Variable_shadowingを参照してください。
スティーブンC

回答:


171

いくつかの選択肢があります。最も意味のあるものは、実際に何をしようとしているかに依存します。

選択肢1:toyNumberをクラスのパブリックメンバー変数にする

class MyToy {
  public int toyNumber;
}

次に、MyToyへの参照をメソッドに渡します。

void play(MyToy toy){  
    System.out.println("Toy number in play " + toy.toyNumber);   
    toy.toyNumber++;  
    System.out.println("Toy number in play after increement " + toy.toyNumber);   
}

選択肢2:参照渡しではなく値を返す

int play(int toyNumber){  
    System.out.println("Toy number in play " + toyNumber);   
    toyNumber++;  
    System.out.println("Toy number in play after increement " + toyNumber);   
    return toyNumber
}

この選択では、mainの呼び出しサイトを少し変更して、を読み取る必要がありますtoyNumber = temp.play(toyNumber);

選択3:クラスまたは静的変数にする

2つの関数が同じクラスまたはクラスインスタンスのメソッドである場合、toyNumberをクラスメンバー変数に変換できます。

選択肢4:int型の単一要素配列を作成し、それを渡します

これはハックと見なされますが、インラインクラス呼び出しから値を返すために使用される場合があります。

void play(int [] toyNumber){  
    System.out.println("Toy number in play " + toyNumber[0]);   
    toyNumber[0]++;  
    System.out.println("Toy number in play after increement " + toyNumber[0]);   
}

2
明確な説明。特に、目的の効果を実現するためのコーディング方法について複数の選択肢を提供するのに適しています。私が今取り組んでいるものに直接役立ちます!この質問が締め切られたのは不自然です。
DarenW 2011年

1
あなたの選択1はあなたが伝えようとしていることを行いますが、私は実際に戻ってそれを再確認しなければなりませんでした。この質問では、参照渡しが可能かどうかを尋ねます。したがって、実際には、関数に渡されたおもちゃが存在するのと同じスコープでprintlnsを実行する必要がありました。この方法では、printlnは、COURSEのポイントを実際に証明しないインクリメントと同じスコープで操作されますインクリメント後に印刷されるローカルコピーの値は異なりますが、渡された参照がそうなるという意味ではありません。繰り返しますが、あなたの例は機能しますが、要点を証明していません。
zero298 2013

いいえ、それは正しいと思います。上記の私の回答の各関数「play()」は、元の「play()」関数のドロップイン置換として意図されており、インクリメントの前後の値も出力しました。元の質問のmainには、参照渡しを証明するprintln()があります。
laslowh 2013

選択肢2にはさらに説明が必要です。メインの呼び出しサイトがtoyNumber = temp.play(toyNumber);希望どおりに機能するように変更する必要があります。
ToolmakerSteve 14

1
これは非常に醜く、ハック感があります...なぜ基本的なものが言語の一部ではないのですか?
しんぞう2016

30

Javaはされていない参照して呼び出すことがある唯一の値でコール

しかし、オブジェクト型のすべての変数は実際にはポインターです。

したがって、Mutable Objectを使用すると、必要な動作が表示されます

public class XYZ {

    public static void main(String[] arg) {
        StringBuilder toyNumber = new StringBuilder("5");
        play(toyNumber);
        System.out.println("Toy number in main " + toyNumber);
    }

    private static void play(StringBuilder toyNumber) {
        System.out.println("Toy number in play " + toyNumber);
        toyNumber.append(" + 1");
        System.out.println("Toy number in play after increement " + toyNumber);
    }
}

このコードの出力:

run:
Toy number in play 5
Toy number in play after increement 5 + 1
Toy number in main 5 + 1
BUILD SUCCESSFUL (total time: 0 seconds)

この動作は標準ライブラリでも確認できます。たとえば、Collections.sort(); Collections.shuffle(); これらのメソッドは新しいリストを返しませんが、その引数オブ​​ジェクトを変更します。

    List<Integer> mutableList = new ArrayList<Integer>();

    mutableList.add(1);
    mutableList.add(2);
    mutableList.add(3);
    mutableList.add(4);
    mutableList.add(5);

    System.out.println(mutableList);

    Collections.shuffle(mutableList);

    System.out.println(mutableList);

    Collections.sort(mutableList);

    System.out.println(mutableList);

このコードの出力:

run:
[1, 2, 3, 4, 5]
[3, 4, 1, 5, 2]
[1, 2, 3, 4, 5]
BUILD SUCCESSFUL (total time: 0 seconds)

2
これは質問に対する答えではありません。単一の要素を含む整数配列を作成し、メソッド内でその要素を変更することを提案した場合、それは質問に答えたでしょうplay。例えばmutableList[0] = mutableList[0] + 1;。アーネスト・フリードマン・ヒルが示唆するように。
ToolmakerSteve、2014

非プリミティブ。オブジェクトの値は参照ではありません。「パラメータ値」は「参照」です。参照は値で渡されます。
vlakov 2015年

私は同様のことをしようとしていますが、コード、メソッドでprivate static void play(StringBuilder toyNumber)-たとえば、整数を返すpublic static intの場合、どのように呼び出しますか?数値を返すメソッドがありますが、どこかで呼び出さないと、使用されていません。
frank17 2015年

18

作る

class PassMeByRef { public int theValue; }

次に、そのインスタンスへの参照を渡します。引数を使用して状態を変更するメソッドは、特に並列コードでは、避けるのが最善であることに注意してください。


3
私が反対票を投じました。これは非常に多くのレベルで間違っています。Javaは値渡しです-常に。例外なく。
duffymo 2011

14
@duffymo-もちろん、好きなようにダウン投票することができます-しかし、OPが求めたことを考慮しましたか?彼は参照によって渡してintしたいと思っています-そして、私が上記のインスタンスへの参照を値で渡した場合、これは達成されます。
Ingo

7
@duffymo:えっ?あなたは間違っているか、質問を誤解しています。これは、ISどのようなOPの要求を行うためのジャワの伝統的な方法の一つ。
ToolmakerSteve

5
@duffymo:あなたのコメントは非常に多くのレベルで間違っています。SOは有用な回答とコメントを提供することであり、それはあなたのプログラミングスタイルと哲学に合わないと(おそらく)正しい回答に反対票を投じるだけです。少なくとも、より良い説明、または元の質問に対するより良い答えを提供できますか?
paercebal

2
@duffymoはまさに私が言ったことです:値で参照を渡します。参照渡しは、それを遅くする言語でどのように行われると思いますか?
2015

11

Javaでは、参照によってプリミティブを渡すことはできません。もちろん、オブジェクト型のすべての変数は実際にはポインターですが、「参照」と呼び、常に値で渡されます。

参照によってプリミティブを本当に渡す必要がある状況では、人々が時々行うことは、パラメーターをプリミティブ型の配列として宣言し、次に引数として単一要素の配列を渡すことです。したがって、参照int [1]を渡し、メソッドで配列の内容を変更できます。


1
いいえ-すべてのラッパークラスは不変です -オブジェクトが作成されると変更できない固定値を表します。
アーネストフリードマンヒル

1
「Javaで参照によってプリミティブを渡すことはできません」:OPはJavaとC ++(可能な場合)を対比しているため、これを理解しているようです。
Raedwald 14

アーネストが言った「すべてのラッパークラスは不変」であることに注意する必要があります。JDKの組み込みの整数、ダブル、ロングなどに適用されます。Ingoの回答のように、カスタムラッパークラスでこの制限をバイパスできます。
user3207158

10

迅速な解決策として、AtomicIntegerまたは任意のアトミック変数を使用して、組み込みのメソッドを使用してメソッド内の値を変更できます。これがサンプルコードです:

import java.util.concurrent.atomic.AtomicInteger;


public class PrimitivePassByReferenceSample {

    /**
     * @param args
     */
    public static void main(String[] args) {

        AtomicInteger myNumber = new AtomicInteger(0);
        System.out.println("MyNumber before method Call:" + myNumber.get());
        PrimitivePassByReferenceSample temp = new PrimitivePassByReferenceSample() ;
        temp.changeMyNumber(myNumber);
        System.out.println("MyNumber After method Call:" + myNumber.get());


    }

     void changeMyNumber(AtomicInteger myNumber) {
        myNumber.getAndSet(100);

    }

}

出力:

MyNumber before method Call:0

MyNumber After method Call:100

私はこのソリューションを使用し、気に入っています。これは、優れた複合アクションを提供し、すでにまたは将来これらのアトミッククラスを使用する可能性がある並行プログラムに統合するのが簡単だからです。
RAnders00 2016年

2
public static void main(String[] args) {
    int[] toyNumber = new int[] {5};
    NewClass temp = new NewClass();
    temp.play(toyNumber);
    System.out.println("Toy number in main " + toyNumber[0]);
}

void play(int[] toyNumber){
    System.out.println("Toy number in play " + toyNumber[0]);
    toyNumber[0]++;
    System.out.println("Toy number in play after increement " + toyNumber[0]);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.