Как класс может иметь член своего собственного типа, разве это не бесконечная рекурсия? - PullRequest
40 голосов
/ 20 марта 2012

Скажем, я определяю класс, в состав которого входит переменная того же типа, что и он сам.

public class abc {
    private abc p;
}

На самом деле это работает, к моему большому удивлению.

Почему я думаю, что это не следует: создавая экземпляр abc, он содержит переменную типа abc, которая содержит переменную типа abc, которая содержит переменную типа abc, который .....

Очевидно, что я ошибаюсь, кто-то может объяснить мне, как?

Ответы [ 6 ]

34 голосов
/ 20 марта 2012

Вы только объявляете переменную, а не создаете ее.Попробуйте создать его в объявлении или в конструкторе, и дайте мне знать, что происходит:

public class Abc {
   private Abc p = new Abc(); // have fun!

   public static void main(String[] args) {
      new Abc();
   }
}

Кстати, если вы не создаете его в классе, а принимаете ссылку на него в методе получения илипараметр конструктора, ваш код будет работать нормально.Вот как работают некоторые связанные списки.

19 голосов
/ 20 марта 2012

Разница заключается в проверках времени компиляции и времени выполнения.

В первом случае (время компиляции) вы заявляете, что в этом случае у вас будет ссылка на значение типа abc.Компилятор узнает об этом, когда проверяет правильную семантику, и, поскольку он знает тип во время компиляции, он не видит проблем с этим.

Во втором случае (во время выполнения) вы фактически создадитезначение для этой ссылки для ссылки.Вот где вы могли бы потенциально получить себе неприятности.Например, если вы сказали следующее:

public class abc {
    private abc p;

    public abc() {
        p = new abc();
    }
}

Это может привести к неприятностям по той причине, которую вы указали (рекурсия, которая не содержит базового случая и будет постоянно выделять память до тех пор, пока вы не запустите ВМпространства кучи).

Однако вы все равно можете сделать нечто подобное и избежать бесконечной рекурсии.Избегая создания значения во время построения, вы откладываете его до тех пор, пока не будет вызван метод.Фактически, это один из распространенных способов реализации шаблона синглтона в Java.Например:

public class abc {
    private abc p;

    private abc() {  // Private construction. Use singleton method
    }

    public static synchronized abc getInstance() {
        if (p == null)
            p = new abc();

        return p;
    }
}

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

10 голосов
/ 20 марта 2012

Если вы хотите смоделировать некоторые реальные сценарии, вам, возможно, придется использовать это понятие. Например, подумайте о ветке дерева. Ветвь дерева может иметь n ветвей. Или из информатики, подумайте об узле связанного списка. Узел будет иметь ссылку на узел рядом с ним. В конце следующий будет содержать нуль, чтобы указать конец списка.

Итак, это только ссылка, указывающая, что этот тип может относиться к одному из его собственных. Ничего больше.

1 голос
/ 21 сентября 2018

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

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

Итак ...

public class abc
{
    public static abc p = new abc ();
}

Работает просто отлично, потому что на переменную 'p' в действительности не влияет создание экземпляра класса abc. Это почти эквивалентно созданию объекта 'p' в другом классе.

Помните, что я могу сделать это ...

 public class abc
 {
     public static abc p = new abc ();

     public static void main(String [] args)
     {
         abc.p;
     }
 }

Без создания нового объекта abc в методе main. Вот как работают статики, они практически не зависят от создания объектов, и в случае вашего вопроса они являются исключением из правила рекурсии.

1 голос
/ 03 ноября 2017

Обобщая некоторые ответы здесь.

Класс содержит ссылку на один из его собственных видов. Лучшая аналогия в реальном мире - охота за сокровищами.Место подсказки содержит некоторые данные и подсказку, которая ведет к другому месту подсказки, которое, как вы знаете, повторяется.

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

0 голосов
/ 20 марта 2012

Пока мы говорим о «рекурсии», «выполнение» кода - это точка, которую мы рассматриваем. Да, это "динамический", если происходит "рекурсия".

В объявлении типа переменная, которая содержит член, который является типом содержащей переменную, является "статической". Например, коробка может содержать другую коробку внутри, и так далее. Но, наконец, есть маленькая коробка, которая ничего не содержит. Для цепочки железнодорожных вагонов каждая вагон имеет члена к следующей вагоне, кроме последней.

В программе статические данные должны быть «конечными» (у вас нет «бесконечного» пространства памяти). Выполнение кода может быть «бесконечным». Но есть исключение. Просто воображаем круг цепи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...