那么在Java中有没有办法做到这一点呢?
如果有,怎么做?
我知道有关final关键字,但据我所知,当应用于方法时,它:
1. 防止在子类中覆盖/多态化该方法。 2. 使该方法可内联。(请参阅下面@Joachim Sauer的评论)
但它不会限制该方法从返回对数据成员的引用,以便调用方不能修改它。
我是否忽视了一些显而易见的东西?
Java中没有类似于C语言const
的"类型修饰符"(可悲)。
你唯一能做的就是返回一个不可变对象或一个不可变包装器来包装可变对象。
不过,在Java中,不可变性不是一种语言特性,你必须依赖库来实现它。
一些不可变对象的例子包括:
Integer
, Character
, ..String
File
URL
常用的不可变包装器(即对可变类型进行包装以防止其改变)是由Collections.unmodifiable*()
方法返回的。
这在Java中不存在。final
和const
有不同的语义,除非应用于原始类型的变量。Java的解决方案通常涉及创建不可变类 - 在构造中初始化对象并不提供访问器以允许更改。这样的类的示例是例如String
或Integer
。
您可以返回一个不可变对象,或者返回私有实例变量的副本。这样,对象的内部状态就可以被“保护”不受修改的影响。
private MyMutableObject mutable = ...
public MyMutableObject getMutableObject() {
return new MyMutableObject(this.mutable);
}
`
你没有忽略任何东西。在纯Java中没有方法可以做到这一点。可能有一些库使用注释提供了部分功能,但我目前不知道有哪些。
将引用传回不可变数据的方法是使你传回的类不可变,简单明了。有几个库函数可以帮助你在一些非常有限但常见的情况下生成不可变的 视图 数据。以下是一个示例:
private List<String> internalData;
public List<String> getSomeList() {
return Collections.unmodifiableList(internalData);
}
我认为在Java中对于非原始对象没有这样的方法(你总是传递这些对象的引用)。你能做到的最接近的方法是返回对象的副本(使用clone或类似的东西); 但那不是很符合Java的惯例。
如果您只想让成员对象的“可见”部分可访问,您可以创建一个具有可见部分的接口,并返回此接口。例如:
public interface Bar {
public int getBing();
}
public class BarImpl implements Bar {
private int bing;
public int getBing() {
return bing;
}
public void setBing(int bing) {
this.bing = bing;
}
}
public class Foo {
private BarImpl bar;
public Bar getNonModifiableBar() {
return bar; // Caller won't be able to change the bing value, only read it.
}
}
防止修改由返回的对象负责。Java除了类型缺乏mutator之外,没有提供声明性/编译时检查不可修改对象的支持。
JDK中有一些支持:像Collection.unmodifiableCollection这样的方法将创建一个对象,如果客户端调用集合mutator方法,则会抛出运行时异常。
如果你真的有动力,可以通过定义只公开/实现访问器方法的只读接口(或类)来获得编译时检查。请记住,仅声明方法返回只读接口将无法防止运行时修改,如果客户端使用内省并且对象提供mutator(不抛出UnsupportedOperationException),则会导致修改。
public List<String> getSomeList();
有
public String getSomeElement(int index);
当你做类似这样的事情:
Object2 obj2 = obj1.getObj2();
obj2 = new Object2();
原始的私有成员(obj1.obj2)保持不变(只是为了确保您掌握了这个概念)。您可以省略对obj2的setter,以使内部字段无法更改。
如果您希望Object2字段是不可变的,则需要应用相同的模式(私有字段,没有setter)。
这回答了您的问题吗?
final
对于 JVM 内联方法并不是必需的。 - Joachim Sauerstatic final
字段如果使用常量表达式进行初始化(称为“编译时常量”),则可以内联,但 Java 中没有指定任何方法内联。 它是 JVM 在运行时的可能运行时优化,但不能影响观察到的行为(这对于final
方法更容易实现,但也适用于非final
方法)。 - Joachim Sauer