Странная проблема указателя при попытке сильно соединить объекты - PullRequest
1 голос
/ 27 марта 2011

В моей программе есть два класса, один называется GlassPiece, а другой называется TrackerChip.

Эти два объекта всегда «прочно связаны», то есть ни один GlassPieces не может совместно использовать TrackerChip, и никакие два TrackerChips не могут совместно использовать GlassPiece. Поэтому в моих методах сеттера мне нужно позаботиться о том, чтобы отключить любые старые ссылки, висящие вокруг, как показано ниже:

public class TrackerChip
{
    GlassPiece linkedGlassPiece;

    public void setGlassPiece(GlassPiece newGlassPiece)
    {
        GlassPiece oldGlassPiece = linkedGlassPiece;

        linkedGlassPiece = newGlassPiece;

        if(oldGlassPiece != null)
        {
            oldGlassPiece.setTrackerChip(null); //disconnect old GlassPiece
        }

        if(linkedGlassPiece != null && linkedGlassPiece.getTrackerChip() != this)
        {
            linkedGlassPiece.setTrackerChip(this); //update counterpart
        }
    }
}

и метод GlassPiece.setTrackerChip (TrackerChip) работает точно так же.

Дело в том, что вышеприведенный код на самом деле не работает, и странные вещи случаются при попытке управлять связью между несколькими разными GlassPieces и TrackerChips. Однако, если я заменю последнюю часть на:

    if(newGlassPiece != null && newGlassPiece.getTrackerChip() != this)
    {
        newGlassPiece.setTrackerChip(this);
    }

Тогда все работает правильно. Это кажется мне очень странным (все, что я сделал, это замениллингенсвязанную переменную instanceGlassPiece на параметр newGlassPiece). Но в начале метода я установил ссылки равными друг другу! Почему первый метод не работает?

P.S. Я могу подтвердить, что в методе нет бесконечного цикла.

Ответы [ 4 ]

1 голос
/ 27 марта 2011

Что касается того, почему это не работает, вы правы, оно не попадет в бесконечный цикл, но оно не будет делать то, что вы ожидаете.

  1. Вы вводите setGlassPiece, connectedGlassPieceдля этого объекта установлено значение newGlassPiece.
  2. Затем он вызывает setTrackerChip (null) для oldGlassPiece.
  3. У oldGlassPiece по-прежнему есть ссылка на оригинальный TrackerChip, поэтому он вызывает setGlassPiece (null), который устанавливает для значение boundGlassPiece значение null, которое вы только что установили в TrackerChip, и также вызывает setTrackerChip (null) для NEW GlassPiece.

Я, честно говоря, не могу придумать, как заставить его работать так, как ты идешь.Вам нужно будет добавить некоторые дополнительные параметры, чтобы он больше не входил.А именно, когда вы вызываете setTrackerChip для oldGlassPiece, он не собирается поворачиваться и вызывать тот же TrackerChip обратно, устанавливая его ссылку на ноль.Возможно, просто логический флаг, который указывает, что он не должен обнулять ссылки второго уровня.

Вот некоторый код:

public class TrackerChip
{
    GlassPiece linkedGlassPiece;

    public void setGlassPiece(GlassPiece newGlassPiece)
    {
         setGlassPiece(newGlassPiece, true);
    }

    public void setGlassPiece(GlassPiece newGlassPiece, boolean reentrant)
    {
        GlassPiece oldGlassPiece = linkedGlassPiece;

        linkedGlassPiece = newGlassPiece;

        if(reentrant && oldGlassPiece != null)
        {
            oldGlassPiece.setTrackerChip(null, false); //disconnect old GlassPiece
        }

        if(linkedGlassPiece != null && linkedGlassPiece.getTrackerChip() != this)
        {
            linkedGlassPiece.setTrackerChip(this); //update counterpart
        }
    }
}
0 голосов
/ 27 марта 2011

Это должно работать, я думаю:

public class TrackerChip {
    GlassPiece linkedGlassPiece;

    public void setGlassPiece(GlassPiece newGlassPiece) {
        if (linkedGlassPiece == newGlassPiece) {
            return;
        }
        if (linkedGlassPiece != null) {
            GlassPiece tmp = linkedGlassPiece;
            linkedGlassPiece = null;
            tmp.setTrackerChip(null);
        }
        if (newGlassPiece != null) {
            linkedGlassPiece = newGlassPiece;
            linkedGlassPiece.setTrackerChip(this);
        }
    }
}
0 голосов
/ 27 марта 2011

Если всегда существует отношение один к одному, возможно, стоит рассмотреть возможность объединения этих классов в один.

0 голосов
/ 27 марта 2011

Вместо этого подхода, я бы порекомендовал просто иметь пару статических HashMaps, которые управляют отношениями. Это, вероятно, было бы гораздо проще. Могут возникнуть проблемы с безопасностью потоков, если ваш вариант использования не является однопоточным, но вам просто нужно синхронизировать метод, который его настраивает. Возможно создать объект управления отношениями следующим образом:

public class RelationshipMgr {
  HashMap<GlassPiece, TrackerChip> gpMap;
  HashMap<TrackerChip, GlassPiece> tcMap;

  public void setRelationship(GlassPiece gp, TrackerChip tc) {
    gpMap.put(gp, tc);
    tcMap.put(tc, gp);
  }
}

На самом деле, в библиотеке Google Guava есть даже класс, готовый к использованию для такого рода вещей, называемый BiMap , посмотрите его.

...