首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何安全地发布可变对象?

如何安全地发布可变对象?
EN

Stack Overflow用户
提问于 2017-11-28 06:01:18
回答 3查看 136关注 0票数 1

我不想做那种深奥的复制方式。

比如说,我有一个可变类型的字段,例如x,y,z坐标。偶尔,我需要把这个领域暴露给一些观众。我希望它是只读的。我记得读过一些类似包装的东西来做这些事情,但我不记得细节。

x,y,z坐标的例子可能太简单了,因为x,y,z是原语类型。因此,getX()总是返回一个副本。

我想要一个通解,即使x,y,z字段是另一种可变类型。

有人能帮忙吗?

编辑

代码语言:javascript
运行
复制
public class Client
{

    public static final Holder holder = new Holder();


    public static void main(String[] args)
    {

        UserWrapper user = holder.getUser();
        System.out.println(user);               //UserWrapper{user=User{address=Address{street='street 101'}}}
        user.getAddress().setStreet("mars");    //UserWrapper{user=User{address=Address{street='mars'}}}
        System.out.println(user);

    }
}
代码语言:javascript
运行
复制
public class Holder
{

    private User user;

    public Holder()
    {
        user = new User();
        Address address = new Address();
        address.setStreet("street 101");
        user.setAddress(address);
    }

    public UserWrapper getUser()
    {
        return new UserWrapper(user);
    }

}
代码语言:javascript
运行
复制
public class User
{

    private Address address;

    public Address getAddress()
    {
        return address;
    }

    public void setAddress(Address address)
    {
        this.address = address;
    }
}
代码语言:javascript
运行
复制
public class UserWrapper
{

    private User user;

    public UserWrapper(User user)
    {
        this.user = user;
    }

    public Address getAddress()
    {
        return user.getAddress();
    }
}

编辑:归功于I don't know who(他删除了答案),我发现他在最初的文章中提到的此链接非常有用。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-11-28 07:23:42

传统方式:

  • 深度复制-防止突变影响正在读取的客户端。
  • 不可变对象--而不是对客户端进行复制,而是复制到update,客户端得到一个旧的指针引用。
  • customer -您提供自己的迭代器/导航接口,它对嵌入数据结构的“版本”字段非常敏感。在访问每个元素之前,它会检查自迭代器创建以来版本是否发生了更改(java集合会这样做)。
  • 强同步--当读取器正在读取时,读取器对数据结构持有锁,以防止更新。通常情况下,这是一个糟糕的解决方案,但偶尔会有用(包括完整性)。
  • 延迟复制--构造一个对象,该对象主要引用原始值,但作为侦听器被触发到原始对象,这样,当对原始文件进行突变时,就会在本地复制预突变值。这就像一种懒散的深拷贝策略。

还有其他人,但这应该会让你开始。

票数 2
EN

Stack Overflow用户

发布于 2017-11-28 07:04:30

Java中没有内置机制使您能够做到这一点。通常,如果您移动实例,您可以:

  1. 使用不可变对象
  2. 传递对象的副本

由于您不想/不能选择这两种方式,所以您需要使用另一种方法。根据您的需求和类结构的复杂程度,有很多不同的实现方法,但是一般的方法是发布一个不变的包装器,而不是原来的包装。

下面是一些示例:

代码语言:javascript
运行
复制
public class XYZ {
    public int x, y, z;
}

public class XYZWrapper {
    private XYZ xyz;

    public XYZWrapper(XYZ xyz) {
        this.xyz = xyz;
    }

    public int getX() { return x; }
    public int getY() { return y; }
    public int getZ() { return z; }
}

public class Address {
    public String name;
    public XYZ xyz;
}

public class AddressWrapper {
    private String name; // Note that this could be public since any String is immutable
    private XYZWrapper xyzWrapper;

    public AddressWrapper(String name, XYZ xyz) {
        this.name = name;
        this.xyzWrapper = new XYZWrapper(xyz);
    }

    public String getName() { 
        return name;
    }

    public XYZWrapper getXYZWrapper() { 
        return xyzWrapper;
    }
}

现在,如果您使用的不是XYZAddress类,您可以使用接口,您可以有两个实现(例如,XYZMutable & XYZImmutable),它将允许您抽象出要返回的类的类型,还将使您能够从XYZMutable实例创建XYZImmutable实例(假设接口只定义&所有getter方法)。

有关此方法的另一个注意事项(特别是如果您使用接口作为首选方法):即使您有一个复杂的类层次结构,也可以相对轻松地创建一个生成器类,该类接收接口实例、可变实现实例并返回一个不变的实现实例作为返回值。

票数 2
EN

Stack Overflow用户

发布于 2017-11-28 07:38:01

也许你在想"抄写即写“这个成语。这允许您避免复制,除非您必须这样做。通常不推荐使用它,因为它不是线程安全的,除非您使用同步,这会不必要地减缓单线程应用程序的速度。

它的工作方式是保持其内部数据的引用计数;类似于以下未经测试的代码:

代码语言:javascript
运行
复制
public class User
{
    private int addressReferenceCount;
    private Address address;

    public User(Address address) {
        addressReferenceCount = 0;
        this.address = address;
    }

    public Address getAddress() {
        addressReferenceCount++;
        return address;
    }

    public void setAddress(Address address)
    {
        if (addressReferenceCount == 0) {
            this.address = address;
        }
        else {
            this.address = new Address(address);
            addressReferenceCount = 0;
        }
    }
}

这可以确保这样的用户代码在必要时获得不同的地址:

代码语言:javascript
运行
复制
User u = new User(new Address("1 Acacia Avenue"));
Address oldAddress = u.getAddress();
Address stillOldAddress = u.getAddress();
u.setAddress(new Address("2 Acacia Avenue"));
Address newAddress = u.getAddress();
assert (oldAddress == stillOldAddress); // both refer to same object
assert (oldAddress != newAddress);
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47524919

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档