如果将对象引用传递给方法,是否可以将该对象设为“只读”方法?
答案 0 :(得分:18)
严格来说。也就是说,可以改变对象的引用不能转换为不能改变对象的引用。此外,除了使用约定之外,没有办法表明类型是不可变的或可变的。
确保某种形式的不变性的唯一功能是final
字段 - 一旦编写,就无法修改。
也就是说,有一些方法可以设计类,以防止不需要的突变。以下是一些技巧:
Defensive Copying 的。传递一个对象的副本,这样如果它被突变,它就不会破坏你的内部不变量。
使用访问修饰符和/或接口仅显示只读方法。您可以使用访问修改(public
/ private
/ protected
),可能与界面结合使用,以便只有某些方法对另一个对象可见。如果暴露的方法本质上是只读的,那么您就是安全的。
默认情况下使您的对象不可变。对象上的任何操作实际上都返回该对象的副本。
另外,请注意,SDK中的API有时会返回对象的不可变版本的方法,例如: Collections.unmodifiableList
。尝试改变不可变列表将引发异常。这不会静态强制执行不变性(在编译时使用静态类型系统),但是它是一种廉价而有效的方法(在运行时)动态强制执行它。
有许多关于Java扩展的研究提案,以更好地控制别名和可访问性。例如,添加readonly
关键字。据我所知,它们都没有包含在未来的Java版本中。如果您有兴趣,可以查看这些指针:
Checker Framework非常有趣。在Checker Framework中,查看Generic Universe Types checker,IGJ immutability checker和Javari immutability checker。该框架使用注释工作,因此它不具有侵入性。
答案 1 :(得分:6)
不,不是没有装饰,合成,克隆等。
答案 2 :(得分:6)
没有一般的机制。您需要编写特殊情况代码来实现它,比如编写一个不可变的包装器(参见Collections.unmodifiableList
)。
答案 3 :(得分:4)
在大多数情况下,您可以通过将Object
克隆为方法的第一个语句来实现类似的操作,例如...
public void readOnlyMethod(Object test){
test = test.clone();
// other code here
}
因此,如果您致电readOnlyMethod()
并传入任何Object
,则会获取Object
的克隆。克隆使用与方法参数相同的名称,因此不存在意外更改原始Object
的风险。
答案 4 :(得分:3)
没有。但是你可以尝试在传递之前克隆该对象,因此该方法所做的任何更改都不会影响原始对象。
答案 5 :(得分:2)
使它实现一个只有只读方法(没有setter方法)的接口,这给出了一个对象的副本(仅限道路的副本)并返回了只读的接口实例,而不是返回一个对象本身的实例< / p>
答案 6 :(得分:1)
您可以将对象的所有参数定义为final
,但这会使对象只读给所有人。
答案 7 :(得分:1)
我相信您真正的问题是避免转义引用。
正如一些答案中指出的那样,它从类中提取一个Interface并仅公开get方法。这样可以防止意外修改,但又不是避免上述问题的万无一失的解决方案。
请考虑以下示例:
Customer.java:
public class Customer implements CustomerReadOnly {
private String name;
private ArrayList<String> list;
public Customer(String name) {
this.name=name;
this.list = new ArrayList<>();
this.list.add("First");
this.list.add("Second");
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public ArrayList<String> getList() {
return list;
}
public void setList(ArrayList<String> list) {
this.list = list;
}
}
CustomerReadOnly.java:
public interface CustomerReadOnly {
String getName();
ArrayList<String> getList();
}
Main.java:
public class Test {
public static void main(String[] args) {
CustomerReadOnly c1 = new Customer("John");
System.out.println("printing list of class before modification");
for(String s : c1.getList()) {
System.out.println(s);
}
ArrayList<String> list = c1.getList();
list.set(0, "Not first");
System.out.println("printing list created here");
for(String s : list) {
System.out.println(s);
}
System.out.println("printing list of class after modification");
for(String s : c1.getList()) {
System.out.println(s);
}
}
}
输出:
printing list of class before modification
First
Second
printing list created here
Not first
Second
printing list of class after modification
Not first
Second
因此,如您所见,只有在没有任何可变成员变量的情况下,提取接口并仅公开get方法才有效。
如果您有一个集合作为成员变量,并且不想从类中逃脱引用,则可以使用ewernli答案中指出的Collections.unmodifiableList()
。
没有外部代码可以修改基础集合,并且您的数据是完全只读的。
但是对于自定义对象也可以做到这一点,我也只知道Interface方法,它可以防止意外修改,但不能确定避免引用转义的万无一失的方法。
答案 8 :(得分:0)
取决于您希望规则强制执行的位置。如果您正在协作处理项目,请使用final
和评论告诉下一个人他们不打算修改此值。否则你不会简单地将方法写入而不是触摸对象吗?
public static void main(String[] args) {
cantTouchThis("Cant touch this");
}
/**
*
* @param value - break it down
*/
public static void cantTouchThis(final String value) {
System.out.println("Value: " + value);
value = "Nah nah nah nah"; //Compile time error
}
特别是对于这种方法,该值永远不会被写入,并且在编译时强制执行,使得解决方案非常健壮。在此方法范围之外,对象保持不变,无需创建任何类型的包装器。
答案 9 :(得分:0)
private boolean isExecuteWriteQueue = false;
public boolean isWriting(){
final boolean b = isExecuteWriteQueue;
return b;
}
答案 10 :(得分:0)
扩展ewernli的answer ...
如果拥有这些类,则可以使用只读接口,以便使用对象的只读引用的方法只能获取子级的只读副本;而主类返回可写版本。
示例
public interface ReadOnlyA {
public ReadOnlyA getA();
}
public class A implements ReadOnlyA {
@Override
public A getA() {
return this;
}
public static void main(String[] cheese) {
ReadOnlyA test= new A();
ReadOnlyA b1 = test.getA();
A b2 = test.getA(); //compile error
}
}
如果您不拥有这些类,则可以扩展该类,覆盖设置器以引发错误或不操作,并使用单独的设置器。这将有效地使基类引用只读的基类,但是这很容易导致混乱和难以理解的错误,因此请确保已对其进行了充分的文档记录。