Java 是否可以制作一个对象;只读;一种方法
如果将对象引用传递给方法,是否可以将对象设为该方法的“只读”对象?否。但是,您可以在传递对象之前尝试克隆该对象,这样该方法所做的任何更改都不会影响原始对象。否,除非进行装饰、合成、克隆,等等。没有通用的机制。您需要编写特殊的案例代码来实现它,就像编写一个不可变的包装器一样(请参见Java 是否可以制作一个对象;只读;一种方法,java,object,reference,Java,Object,Reference,如果将对象引用传递给方法,是否可以将对象设为该方法的“只读”对象?否。但是,您可以在传递对象之前尝试克隆该对象,这样该方法所做的任何更改都不会影响原始对象。否,除非进行装饰、合成、克隆,等等。没有通用的机制。您需要编写特殊的案例代码来实现它,就像编写一个不可变的包装器一样(请参见Collections.unmodifiableList).您可以将对象的所有参数定义为final,但这会使对象对每个人都是只读的。在大多数情况下,您可以通过将对象克隆为方法的第一条语句来实现类似的效果,例如 publi
Collections.unmodifiableList
).您可以将对象的所有参数定义为final
,但这会使对象对每个人都是只读的。在大多数情况下,您可以通过将对象
克隆为方法的第一条语句来实现类似的效果,例如
public void readOnlyMethod(Object test){
test = test.clone();
// other code here
}
因此,如果调用
readOnlyMethod()
并传入任何对象
,则将获取对象
的克隆。克隆使用与方法参数相同的名称,因此不会意外更改原始对象
严格来说。也就是说,不能将可以改变对象的引用转换为不能改变对象的引用。此外,除了使用约定之外,没有其他方法来表示类型是不可变的或可变的
确保某种形式的不变性的唯一功能是final
字段-一旦写入,就不能修改它们
也就是说,有一些方法可以设计类,以防止不必要的突变。以下是一些技巧:
- 。传递对象的副本,这样,如果对象发生了变异,它就不会破坏内部不变量
- 使用访问修饰符和/或接口仅公开只读方法。您可以将访问修饰符(
/public
/private
)与接口结合使用,以便只有某些方法对其他对象可见。如果公开的方法本质上是只读的,那么您是安全的protected
- 默认情况下,使对象不可变。对对象的任何操作实际上都会返回对象的副本
Collections.unmodifiableList
。试图改变不可变列表将引发异常。这不是静态地(在静态类型系统的编译时)强制执行不变性,而是动态地(在运行时)强制执行不变性的一种廉价而有效的方法
有许多关于Java扩展的研究建议,以更好地控制别名和可访问性。例如,添加一个readonly
关键字。据我所知,它们中没有一个计划包含在Java的未来版本中。如果您感兴趣,可以查看这些指针:
- --它列出并比较了大多数提案
- --扩展类型系统的非侵入式方法,特别是使用不可变类型
Checker框架非常有趣。在Checker框架中,查看泛型宇宙类型检查器、IGJ不变性检查器和Javari不变性检查器。该框架使用注释工作,因此不具有侵入性。这取决于您希望在何处实施规则。如果您正在协作处理项目,请使用带有注释的
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
}
特别是对于这个方法,这个值永远不会被写入,它在编译时被强制执行,使得解决方案非常健壮。在该方法的范围之外,对象保持不变,无需创建任何类型的包装器。使其实现一个只有只读方法(无setter方法)的接口,这将提供对象的副本(仅道路副本)返回接口的只读实例,而不是返回对象本身的实例
private boolean isExecuteWriteQueue = false;
public boolean isWriting(){
final boolean b = isExecuteWriteQueue;
return b;
}
如果您拥有这些类,则可以使用只读接口,这样使用对象只读引用的方法只能获得子类的只读副本;而主类返回可写版本
范例
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
}
}
如果您不拥有这些类,则可以扩展该类,重写setter以抛出错误或不执行操作,并使用单独的setter。这将有效地使基类引用成为只读引用,但是这很容易导致混淆和难以理解的错误,因此请确保它有良好的文档记录。我相信您真正的问题是避免转义引用 正如在一些回答中指出的,从类中提取接口并只公开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;
}
}
public interface CustomerReadOnly {
String getName();
ArrayList<String> getList();
}
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方法才有效
如果您有一个集合作为成员变量,您不想从类中获取其引用,那么可以使用Collections.unmodifiableList()
,正如ewernli在回答中指出的那样
使用此选项,外部代码无法修改基础集合,并且您的数据是完全只读的
但是,当涉及到用于执行相同操作的自定义对象时,我只知道接口方法可以防止意外修改,但不确定避免引用转义的简单方法。这要求对象实际正确地实现
克隆,并且,由于这是一个f*ed-up API,现代阶级很少为此烦恼。然而,我希望大多数核心Java类应该正确地实现clone()
,这样就可以涵盖大多数情况