Подтвердить что ты не робот

Любой хороший способ сделать два неизменных объекта касаются друг друга?

Возьмите эти два класса Java:

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

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

Есть ли способ без использования отражения *, чтобы снять это? На самом деле я этого не ожидаю, но это не может помешать спросить.

Обновление: поскольку в построении байткода есть два шага (1. выделить объект, 2. call constructor **), может ли это быть (ab) использоваться для этого, с рукописным байт-кодом или пользовательским компилятором? Я говорю о выполнении первого шага для обоих объектов, а затем для шага 2 для обоих, используя ссылки с шага 1. Конечно, что-то подобное было бы довольно громоздким, и эта часть вопроса является академической.

(* Потому что отражение может вызвать проблемы с менеджером безопасности)

(** говорит мои ограниченные знания)

4b9b3361

Ответ 1

Это может работать только чисто, если один из объектов создается другим. Например, вы можете изменить класс User на что-то вроде этого (сохраняя класс Inventory неизменным):

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

Однако вам нужно быть осторожным при доступе к объекту User в конструкторе Inventory: он еще не полностью инициализирован. Например, поле Inventory все равно будет null!

Обновление объявлений. Теперь я подтвердил, что метод управления байтовым кодом не работает . Я пробовал его с помощью Jasmin, и он всегда не загружался с помощью VerifyError.

Вникая в проблему, я нашел § 4.10.2.4 Методы инициализации экземпляра и недавно созданные объекты. В этом разделе объясняется, как JVM обеспечивает передачу только инициализированных экземпляров объектов.

Ответ 2

Вы можете сделать это, если вам не нужно вводить один из объектов.

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

Ответ 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;
    }
}

Это лучшее, что я могу придумать. Может быть, лучший образец.

Ответ 4

Слегка педантичный, но не строго говоря, необходимо создать одно внутри другого, если вы не возражаете против небольшой косвенности. Они оба могут быть внутренними классами.

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; }
    ...
}

Ответ 5

Это одно "решение", хотя потеря одного 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;
   }
}

Ответ 6

Если вас интересует только байт-код JVM и не интересуется кодированием в Java, возможно, использование Scala или Clojure может помочь. Вам понадобится какая-то техника letrec.

Ответ 7

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.
}