簡體   English   中英

如何為 Java 中的基元執行相當於按引用傳遞的操作

[英]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 *);

“路過...”保留在JavaC中。 除此之外,如果您打算更改作為引用給出的原語的包裝器實例,這是通過反射完成的。 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM