[英]How to do the equivalent of pass by reference for primitives in Java
這個 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);
}
}
將 output 這個:
Toy number in play 5 Toy number in play after increement 6 Toy number in main 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++ output 將是這樣的:
Toy number in play 5 Toy number in play after increement 6 Toy number in main 6
我的問題是 - Java 中獲得與 C++ 代碼相同的 output 的等效代碼是什么,假設Java 是按值傳遞而不是按引用傳遞?
您有多種選擇。 最有意義的那個真的取決於你想要做什么。
選擇 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 中的 callsite 進行小的更改,以便它讀取, toyNumber = temp.play(toyNumber);
.
選擇 3:使其成為類或靜態變量
如果這兩個函數是同一類或類實例上的方法,則可以將 toyNumber 轉換為類成員變量。
選擇 4:創建一個 int 類型的單元素數組並傳遞它
這被認為是一種 hack,但有時用於從內聯類調用中返回值。
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]);
}
Java 不是按引用調用,它只是按值調用
但是所有對象類型的變量實際上都是指針。
因此,如果您使用可變對象,您將看到您想要的行為
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)
做一個
class PassMeByRef { public int theValue; }
然后傳遞對它的實例的引用。 請注意,最好避免使用通過其參數改變狀態的方法,尤其是在並行代碼中。
在 Java 中不能通過引用傳遞原語。 當然,對象類型的所有變量實際上都是指針,但我們稱它們為“引用”,它們也總是按值傳遞。
在確實需要通過引用傳遞原始類型的情況下,人們有時會將參數聲明為原始類型的數組,然后將單元素數組作為參數傳遞。 所以你傳遞了一個引用int[1],在方法中,你可以改變數組的內容。
對於快速解決方案,您可以使用 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
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]);
}
我們在 checkPassByValue 中設置了 a = 5 但它並沒有體現在 a 的值中。 類似地,當我們傳遞一個人時,我們可以使用 Person.setName 更新該人指向的數據,但是如果更改該人並使其引用一個新的 Person() 在這種情況下更新將不會反映在主要 function
class Person {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
public Person(String name) {
this.name = name;
}
}
public class PassByValueVsReference {
public static void main(String[] args) {
int a = 3;
Person person = new Person("Foo");
checkPassByValue(a, person);
System.out.println(a);
System.out.println(person.getName());
}
public static void checkPassByValue(int number, Person person) {
number = 5;
person.setName("Foo-updated");
person = new Person("Foo_new reference");
}
}
//C++ equivalent of checkPassByValue(int ,Person *);
“路過...”保留在Java和C中。 除此之外,如果您打算更改作為引用給出的原語的包裝器實例,這是通過反射完成的。 Integer
的示例。
public class WrapperTest
{
static void mute(Integer a)
{
try
{
Field fValue = a.getClass().getDeclaredField("value");
fValue.setAccessible(true);
fValue.set(a, 6);
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
Integer z = 5;
mute(z);
System.out.println(z);
}
}
Output 在您的 C++ 示例中是6 。 需要反射,因為 Java 設計將原始包裝器視為不可變的。 否則每一個 class 都可以作為包裝器,即使是像int[]
這樣長度為 1 的數組。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.