【问题标题】:Is there a way to guarantee that a real referenced object has no side effect for a caller function?有没有办法保证一个真正的引用对象对调用者函数没有副作用?
【发布时间】:2020-01-17 06:40:26
【问题描述】:

我是一名最近学习 JAVA 的学生。
我根据我的 C++ 经验接近这种语言。

所以我花了将近四天的时间来了解 c++ 和 java 在按值调用或引用方面的差距。

Java 是按值调用的,因为调用者函数将引用类型变量本身传递给被调用者。

当我理解了上面这句话时,突然想到一个问题。
我的问题是……

我了解到按值调用的优点之一是没有副作用

在JAVA中,保证引用类型变量本身没有副作用。
但是引用变量引用的真实对象可能有副作用
之后返回调用者函数。

那么有没有办法保证堆内存中的引用对象对调用函数也没有副作用?

(如果我对 JAVA 机制有什么误解,请告诉我)

===============================
添加示例

class Person{
    String  name;
    int     age;
    Person(String name, int age){
        this.name = name;
        this.age =age;
    }

}


public static void foo(){
    Person p = new Person("haha", 17);

    System.out.println(p.name); // haba

    boo(p);


    System.out.println(p.name); // hoho, but foo() wants original value "haha"

}

public static void boo(Person p){
    p.name = "hoho";

}

我希望 boo() 函数不要修改 p 实例的成员变量(此处为 p.name)。

【问题讨论】:

  • 不确定我是否遵循您的问题,但您可以通过将参数标记为final 来完成您想要做的事情。更多信息here.
  • “有没有办法保证” - 有。如果您想确保对象在函数中没有更改,您可以创建该对象的副本并改为传递它。另一种方法可能是 - 使对象不可变或声明不允许任何更改的接口。
  • 你能展示一些代码来说明你在问什么吗?对象和变量没有副作用。方法和函数可以。我真的不明白你在问什么,真的。
  • @Sweeper,对不起。我添加了一个例子
  • @Pavel Smirnov 谢谢。您提到的“复制并通过”方式是我想知道的。这是一种简单但很棒的方式。

标签: java call-by-value


【解决方案1】:

下面是一个小例子,说明如何传递对象的副本,确保原始对象不被修改。

当我使用复制构造函数时,原始对象的内部状态被保留,但是当我简单地声明 TestRef otherRef = testRef; 时,只复制引用,这样如果新对象被修改,原始对象也是如此。

请注意,在这种情况下,我确实复制了 String 引用,因为字符串在 java 中是不可变的类。

public class Main {

    public static class TestRef {
        public String a;
        public int b;

        //Copy constructor
        public TestRef(TestRef other) {
            this(other.a, other.b);
        }

        public TestRef(String a, int b) {
            this.a = a;
            this.b = b;
        }

    }

    public static void main(String[] args) throws IOException, TransformerException {

        TestRef testRef = new TestRef("TestRef", 1);
        //Using copyConstructor to make deep copy of object
        System.out.println(testRef.a);
        TestRef deepCopy = new TestRef(testRef);
        modifyClass(deepCopy);
        System.out.println(testRef.a);
        //Shallow copy
        TestRef otherRef = testRef;
        modifyClass(otherRef);
        System.out.println(testRef.a);      
    }

    public static void modifyClass(TestRef testRef) {
        testRef.a = "newString";
        testRef.b++;
    }
}

输出:

TestRef
TestRef
newString

【讨论】:

  • “复制和传递”机制(上面提到)是基于复制构造函数实现的。我知道所有的概念,但将它们组合在一起对我来说还很困难。
  • 我希望这能回答你的问题:)
【解决方案2】:

你可以这样做:

class Person{
    final String name;
    int age;
Person(String name, int age){
        this.name = name;
        this.age =age;
    }
}

【讨论】:

  • 最终对象仍然可以修改但不能重新分配。在这种情况下,将重新分配 String 类型的名称。这是解释吗
猜你喜欢
  • 2017-02-11
  • 2020-03-03
  • 2010-10-27
  • 1970-01-01
  • 1970-01-01
  • 2020-07-09
  • 2023-02-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多