java 是否可以将对象“只读”到方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10533687/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it possible to make an object "Read Only" to a method
提问by Don Li
If an object reference is passed to a method, is it possible to make the object "Read Only" to the method?
如果将对象引用传递给方法,是否可以使对象对方法“只读”?
采纳答案by ewernli
Not strictly speaking. That is, a reference that can mutate an object can not be turned into a reference that can not mutate an object. Also, there is not way to express that a type is immutable or mutable, other than using conventions.
严格来说不是。也就是说,可以改变对象的引用不能变成不能改变对象的引用。此外,除了使用约定之外,没有办法表示类型是不可变的或可变的。
The only feature that ensure some form of immutability would be final
fields - once written they can not be modified.
确保某种形式的不变性的唯一功能是final
字段 - 一旦写入它们就无法修改。
That said, there are ways to design classes so that unwanted mutation are prevented. Here are some techniques:
也就是说,有一些方法可以设计类以防止不需要的突变。这里有一些技巧:
Defensive Copying. Pass a copy of the object, so that if it is mutated it doesn't break your internal invariants.
Use access modifiersand/or interfaceto expose only read-only methods. You can use access modifieres (
public
/private
/protected
), possibly combined with interface, so that only certain methods are visible to the other object. If the methods that are exposed are read-only by nature, you are safe.Make your object immutable by default. Any operation on the object returns actually a copy of the object.
防御性复制。传递对象的副本,这样如果它发生变异,它就不会破坏您的内部不变量。
使用访问修饰符和/或接口仅公开只读方法。您可以使用访问modifieres(
public
/private
/protected
),可能结合界面,因此,只有某些方法是到其他对象可见。如果公开的方法本质上是只读的,那么您是安全的。默认情况下使您的对象不可变。对对象的任何操作实际上都返回对象的副本。
Also, note that the API in the SDK have sometimes methods that return an immutable version of an object, e.g. Collections.unmodifiableList
. An attempt to mutate an immutable list will throw an exception. This does not enforce immutability statically (at compile-time with the static type system), but is is a cheap and effective way to enforce it dynamically (at run-time).
另请注意,SDK 中的 API 有时具有返回对象的不可变版本的方法,例如Collections.unmodifiableList
. 尝试改变不可变列表将引发异常。这不会静态地强制执行不变性(在编译时使用静态类型系统),而是一种廉价且有效的动态(在运行时)强制执行不变性的方法。
There has been many research proposals of Java extension to better control of aliasing, and accessibility. For instance, addition of a readonly
keyword. None of them is as far as I know planned for inclusion in future version of Java. You can have a look at these pointers if you're interested:
已经有许多关于 Java 扩展的研究建议,以更好地控制别名和可访问性。例如,添加readonly
关键字。据我所知,它们都没有计划包含在 Java 的未来版本中。如果你有兴趣,你可以看看这些指针:
- Why We Should Not Add ''Read-Only'' to Java (yet)-- it lists and compare most of the proposals
- The Checker Framework: Custom pluggable types for Java-- a non intrusive way to extend the type system, notably with immutable types.
- 为什么我们不应该将“只读”添加到 Java(还)——它列出并比较了大多数提案
- Checker 框架:Java 的自定义可插入类型——一种扩展类型系统的非侵入性方式,尤其是不可变类型。
The Checker Framework is very interesting. In the Checker Framework, look at Generic Universe Types checker, IGJ immutability checker, and Javari immutability checker. The framework works using annotations, so it is not intrusive.
Checker 框架非常有趣。在 Checker Framework 中,查看 Generic Universe Types checker、IGJ immutability checker 和 Javari immutability checker。该框架使用注释工作,因此它不具有侵入性。
回答by Dave Newton
No, not without decorating, compositing, cloning, etc.
不,并非没有装饰、合成、克隆等。
回答by Marko Topolnik
There's no general mechanism for that. You'll need to write special-case code to achieve it, like writing an immutable wrapper (see Collections.unmodifiableList
).
没有通用的机制。您需要编写特殊情况的代码来实现它,比如编写一个不可变的包装器(请参阅 参考资料Collections.unmodifiableList
)。
回答by wattostudios
You could achieve a similar thing in most cases by cloning the Object
as the first statement of the method, such as this...
在大多数情况下,您可以通过将 克隆Object
为方法的第一条语句来实现类似的功能,例如...
public void readOnlyMethod(Object test){
test = test.clone();
// other code here
}
So if you called readOnlyMethod()
and pass in any Object
, a clone of the Object
will be taken. The clone uses the same name as the parameter of the method, so there's no risk of accidentally changing the original Object
.
因此,如果您调用readOnlyMethod()
并传入 any Object
,Object
则会获取的克隆。克隆使用与方法参数相同的名称,因此不存在意外更改原始Object
.
回答by michael667
No. But you could try to clone the object before passing it, so any changes made by the method won't affect the original object.
不可以。但是您可以在传递对象之前尝试克隆该对象,因此该方法所做的任何更改都不会影响原始对象。
回答by jayant mishra
making it implement a interface which has only read only methods (no setter methods) this gives a copy of an object (road-only copy) and returning the read only instance of interface instead of returning the instance of an object itself
使其实现一个只有只读方法(没有 setter 方法)的接口,这给出了一个对象的副本(仅道路副本)并返回接口的只读实例而不是返回对象本身的实例
回答by Kai
You could define all parameters of the objects as final
but that makes the object read only to everyone.
您可以定义对象的所有参数,final
但这会使对象只读给所有人。
回答by Akki
I believe your real question is about avoiding escape references.
我相信你真正的问题是关于避免转义引用。
As pointed out in some answers to extract an Interface from class and expose only get methods. It will prevent modification by accident but it is again not a foolproof solution to avoid above problem.
正如一些答案中所指出的那样,从类中提取接口并仅公开 get 方法。它可以防止意外修改,但也不是避免上述问题的万无一失的解决方案。
Consider below example:
考虑下面的例子:
Customer.java:
客户.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:
CustomerReadOnly.java:
public interface CustomerReadOnly {
String getName();
ArrayList<String> getList();
}
Main.java:
主.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);
}
}
}
Ouput:
输出:
printing list of class before modification
First
Second
printing list created here
Not first
Second
printing list of class after modification
Not first
Second
So, as you can see extracting interface and exposing only get methods works only if you don't have any mutable member variable.
因此,正如您所看到的,只有当您没有任何可变成员变量时,才能提取接口并仅公开 get 方法。
If you have a collection as a member variable whose reference you don't want to get escape from class, you can use Collections.unmodifiableList()
as pointed out in ewernli's answer.
如果您有一个集合作为成员变量,您不想从类中逃脱其引用,则可以Collections.unmodifiableList()
按照 ewernli 的回答中指出的那样使用。
With this no external code can modify the underlying collection and your data is fully read only.
有了这个,没有外部代码可以修改底层集合,并且您的数据是完全只读的。
But again when it comes to custom objects for doing the same, I am aware of the Interface method only as well which can prevent modification by accident but not sure about the foolproof way to avoid reference escape.
但同样,当涉及到用于执行相同操作的自定义对象时,我也只知道 Interface 方法可以防止意外修改,但不确定避免引用逃逸的万无一失的方法。
回答by Tezra
Expanding on ewernli's answer...
扩展 ewernli 的答案......
If you own the classes, you can use read-only interfaces so that methods using a read-only reference of the object can only get read-only copies of the children; while the main class returns the writable versions.
如果您拥有这些类,则可以使用只读接口,以便使用对象只读引用的方法只能获得子对象的只读副本;而主类返回可写版本。
example
例子
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
}
}
If you don't own the classes, you could extend the class, overriding the setters to throw an error or no-op, and use separate setters. This would effectively make the base class reference the read-only one, however this can easily lead to confusion and hard to understand bugs, so make sure it is well documented.
如果您不拥有这些类,则可以扩展该类,覆盖 setter 以抛出错误或无操作,并使用单独的 setter。这将有效地使基类引用成为只读的,但是这很容易导致混淆和难以理解的错误,因此请确保它有据可查。
回答by styler1972
Depending on where you want the rule enforced. If you are working collaboratively on a project, use final
with a comment telling the next person they are not meant to modify this value. Otherwise wouldn't you simply write the method to nottouch the object?
取决于您希望在何处强制执行规则。如果您正在协作处理一个项目,请使用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
}
So specifically to this method, the value will never be written to, and it is enforced at compile time making the solution extremely robust. Outside the scope of this method, the object remains unaltered without having to create any sort of wrapper.
所以特别是对于这个方法,这个值永远不会被写入,它在编译时强制执行,使解决方案非常健壮。在此方法的范围之外,对象保持不变,无需创建任何类型的包装器。