【问题标题】:Setter params final in JavaJava中的setter参数final
【发布时间】:2015-09-15 04:31:07
【问题描述】:

我一直在用java编程,最近开始学习一些c++。

在 C++ 中,将 setter 参数设置为 const 是惯例,为什么我们在 java 中看不到这一点?

我的意思是像这样创建 setter 有什么缺点:

public void setObject(final Object o){ this.o=o; }

public void setObject(Object o){ this.o=o; }

第一个应该强制 Object param o 在整个 set 函数中保持不变,而不是?

编辑:

最后一个参数将强制这不会发生:

public void setName(String name){ 
     name="Carlos";
     this.name=name; 
}

用户将永远无法设置不同于“Carlos”的名称

【问题讨论】:

  • 因为在 c++ 中,您可以将参数作为引用和指针发送,这使得开发人员可以更改原始值,有时会出错
  • 因为Java总是按值传递,所以在被调用方法中修改引用本质上是一个空操作。通过引用修改状态的行为与 C++ 中的行为相同。
  • 您应该删除 C++ 标签,因为这是关于 Java 的;它是一种不同于 C++ 的语言。
  • final 在 Java 中的含义与 const 在 C++ 中的含义不同。

标签: java constants final setter


【解决方案1】:

将 Java 方法参数设置为 final 没有什么好处,因为它不会阻止某人在方法中更改参数引用的状态。它所阻止的只是将参数变量重新分配给其他东西,这对原始引用没有任何作用,并且它允许在匿名内部类中使用参数。如果您希望在这种情况下真正安全,请尽可能使您的参数类型不可变。


编辑
您已发布:

public void setObject(Object o){ 
     o++; // this does not compile
     this.o=o; 
}

混合了原始数字和引用类型。仅当 o 是 Integer 或其他数字包装类时才有意义,即使如此,将其设为 final 也不会阻止某人创建:

private void setI(final Integer i) {
   this.i = 1 + i;
}

但是你的代码和上面这段代码都不会影响调用代码端的参数对象。


编辑
好的,现在你已经发布了:

public void setName(String name){ 
     name="Carlos";
     this.name=name; 
}

但是有人可以写

public void setName(final String name){ 
     this.name= name + " Carlos"; 
}

这就是危险来的地方,而 final 也无济于事。假设你有一个名为 Name 的类:

public class Name {
   private String lastName;
   private String firstName;
   public Name(String lastName, String firstName) {
      this.lastName = lastName;
      this.firstName = firstName;
   }
   public String getLastName() {
      return lastName;
   }
   public void setLastName(String lastName) {
      this.lastName = lastName;
   }
   public String getFirstName() {
      return firstName;
   }
   public void setFirstName(String firstName) {
      this.firstName = firstName;
   }
}

然后是一个类 Foo,它带有一个 Name 字段和一个 setter。这是危险代码:

class Foo {
   private Name name;

   public void setName(final Name name) {
      name.setFirstName("Carlos");
      this.name = name;
   }
}

因为它不仅改变了字段的状态,它还改变了调用代码中 Name 引用的状态,而 final 修饰符一点作用也没有。解决方案:使 Name 不可变。

例如,

import java.util.Date;

// class should be declared final
public final class BetterName {
   private String lastName;
   private String firstName;
   private Date dateOfBirth;

   public BetterName(String lastName, String firstName, Date dob) {
      this.lastName = lastName;
      this.firstName = firstName;

      // make and store a private copy of non-immutable reference types
      dateOfBirth = new Date(dob.getTime()); 
   }

   // only getters -- no setters
   public String getLastName() {
      return lastName;
   }

   public String getFirstName() {
      return firstName;
   }

   public Date getDateOfBirth() {
      // return copies of non-immutable fields
      return new Date(dateOfBirth.getTime());
   }
}

【讨论】:

  • 是的,但这意味着在你的设置器中将你的参数设置为 final 是明智的(这样你就不会失去最初的给定值)?那我们为什么不把这看作一个约定呢?
  • @KoenDemonie:我不明白你上面的说法。将 setter 参数设为 final 实际上没有任何优势。 "so you don't lose the initial given value" 是什么意思?同样,最重要的一点是尽量让你的类在任何时候和任何地方都是不可变的。
  • 好吧,如果它不是 final 一个 setter 函数可以改变它的值,并失去最初的给定值......没什么太大但添加 final 不会伤害并且可以解决这个问题
  • 将参数设置为 final 将强制执行您的声明:“setter sets,就是这样”。嗯,这就是我的想法......
  • 关于您的BetterName 示例:由于您不打算添加setter 方法,因此如果每个字段都是final 本身,您可以更好地证明这一点。所以其他开发者会知道这个模型是不可变的。
【解决方案2】:

好的,不能分配final 参数/变量。由于 java 编译器需要能够确定变量/参数是否实际上是最终的(对于匿名内部类),因此优化不是 AFAIK 的因素。

更多的是 C++ 有一个更大的工具集,java 试图减少它。因此使用 C++ const string& 很重要,说

  1. 字符串通过指针传递,访问被自动取消引用。
  2. 如果实际参数是变量,则变量本身不会改变。
  3. 请注意,可能存在用于传递 const string& 以外的其他内容的转换运算符。

现在java:

  1. Java 不在堆栈上分配对象,只在堆栈上保留原始类型和对象句柄。
  2. Java 没有输出参数:传递给方法调用的变量永远不会改变它的立即值。

回到你的问题:

作为 java 中的 setter,大多数情况下不会从 final 参数中受益。 final 将是不使用该变量进行第二次分配的合同。

但是:

public final void setXyz(Xz xyz) {
    this.xyz = xyz;
}

更有用:此方法不能被覆盖,因此可以安全地在构造函数中使用。 (在构造函数中调用被覆盖的方法将在尚未初始化的子实例的上下文中。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-03
    相关资源
    最近更新 更多