制作两个不可变对象的任何好方法互相引用?

时间:2011-11-02 10:10:37

标签: java reflection immutability final

采用这两个Java类:

class User {
   final Inventory inventory;
   User (Inventory inv) {
       inventory = inv;
   }
}

class Inventory {
   final User owner;
   Inventory (User own) {
       owner = own;
   }
}

有没有办法without using reflection*解决这个问题?我实际上并不指望它是这样,但是问它也不会有害。

更新:由于在字节码构造中有两个步骤(1.分配对象,2。调用构造函数**)这可能是(ab)用于执行此操作,使用手写字节码或自定义编译器?我说的是首先对两个对象执行步骤1,然后使用步骤1中的参考执行步骤2.当然这样的事情会相当麻烦,而这部分问题是学术性的。

(*因为反思可能会给安全管理员带来麻烦)

(**说我的知识有限)

7 个答案:

答案 0 :(得分:13)

如果其中一个对象是由另一个创建的,那么它只能干净地工作。例如,您可以将User类更改为此类(同时保持Inventory类不变):

class User {
   private final Inventory inventory;
   User () {
       inventory = new Inventory(this);
   }
}

您需要注意访问User构造函数中的Inventory对象,但是:它还没有完全初始化。例如,其inventory字段仍为null

广告更新:我现在已经确认字节码操作方法无法正常工作。我使用Jasmin尝试了它,但始终无法加载VerifyError

深入研究这个问题,我找到了§ 4.10.2.4 Instance Initialization Methods and Newly Created Objects。本节介绍JVM如何确保只传递初始化的对象实例。

答案 1 :(得分:7)

如果您不需要注入其中一个对象,则可以执行此操作。

class User {
   private final Inventory inventory;
   User () {
       inventory = new Inventory(this);
   }
}

答案 2 :(得分:3)

class User {
    private final Inventory inventory;
    User (/*whatever additional args are needed to construct the inventory*/) {
        //populate user fields
        inventory = new Inventory(this);
    }
}

class Inventory {
    private final User owner;
    Inventory (User own) {
        owner = own;
    }
}

这是我能想到的最好的。也许有更好的模式。

答案 3 :(得分:1)

略显迂腐,但如果你不介意一点间接,那么在另一个内部创建一个并不是严格必要的。他们都可以成为内部阶级。

public class BadlyNamedClass {
    private final User owner;
    private final Inventory inventory;

    public BadlyNamedClass() {
        this.owner = new User() {
            ... has access to BadlyNamedClass.this.inventory;
        };
        this.inventory = new Inventory() {
            ... has access to BadlyNamedClass.this.owner;
        };
    }
    ...
}

甚至:

public class BadlyNamedClass {
    private final User owner;
    private final Inventory inventory;

    public BadlyNamedClass() {
        this.owner = new User(this);
        this.inventory = new Inventory(this);
    }
    public User getOwner() { return owner; }
    public Inventory getInventory() { return inventory; }
    ...
}

答案 4 :(得分:0)

这是一个“解决方案”,但丢失一个final是不方便的。

class User {
   Inventory inventory;
   User () { }
   // make sure this setter is only callable from where it should be,
   // and is called only once at construction time
   setInventory(inv) {
       if (inventory != null) throw new IllegalStateException();
       inventory = inv;
   }
}

class Inventory {
   final User owner;
   Inventory (User own) {
       owner = own;
   }
}

答案 5 :(得分:0)

如果您只对JVM字节码感兴趣并且不关心Java中的编码,那么使用 Scala Clojure 可能会有所帮助。你需要某种letrec机器。

答案 6 :(得分:0)

B:“用户创建的库存是我们最后的希望” Y:“不,还有另一个。”

如果您将引用抽象为第三方,则可以控制其中的关系。

例如。

public class User
{
    private final String identifier;  // uniquely identifies this User instance.

    public User(final String myIdentifier)
    {
        identifier = myIdentifier;

        InventoryReferencer.registerBlammoUser(identifier); // Register the user with the Inventory referencer.
    }

    public Inventory getInventory()
    {
        return InventoryReferencer.getInventoryForUser(identifier);
    }
}

public interface Inventory // Bam!
{
    ... nothing special.
}

// Assuming that the Inventory only makes sence in the context of a User (i.e. User must own Inventory).
public class InventoryReferencer
{
    private static final Map<String, Inventory> referenceMap = new HashMap<String, Inventory>();

    private InventoryReferencer()
    {
        throw ... some exception - helps limit instantiation.
    }

    public static void registerBlammoUser(final String identifier)
    {
        InventoryBlammo blammo = new InventoryBlammo();
        referenceMap.add(indentifier, blammo);
    }

    public static void registerKapowUser(final String identifier)
    {
        InventoryBlammo kapow = new InventoryKapow();
        referenceMap.add(indentifier, kapow);
    }

    public static Inentory getInfentoryForUser(final String identifier)
    {
        return referenceMap.get(identifier);
    }
}

// Maybe package access constructors.
public class InventoryBlammo implements Inventory
{
    // a Blammo style inventory.
}

public class InventoryKapow implements Inventory
{
    // a Kapow style inventory.
}