[英]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.