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
    )与接口结合使用,以便只有某些方法对其他对象可见。如果公开的方法本质上是只读的,那么您是安全的

  • 默认情况下,使对象不可变。对对象的任何操作实际上都会返回对象的副本

另外,请注意,SDK中的API有时具有返回对象的不可变版本的方法,例如
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()
,这样就可以涵盖大多数情况