67

Say I define a class that has as a member a variable of the same type as itself.

public class Abc {
    private Abc p;
}

This actually works, much to my surprise.

Why I think it shouldn't: creating an instance of Abc, it contains a variable of type Abc, which contains a variable of type Abc, which contains a variable of type Abc, which .....

Obviously I'm wrong, could someone enlighten me as to how?

Pshemo
  • 122,468
  • 25
  • 185
  • 269
jason
  • 4,721
  • 8
  • 37
  • 45
  • 2
    Good question, but please be sure to go back to your prior questions and accept an answer for each one (including this question). – pseudoramble Mar 20 '12 at 14:43
  • 1
    Because in Java, a variable of type `abc` doesn't contain an `abc` object. A variable of type `abc` contains a *reference to* an `abc` object. Your reasoning would be valid in say C++. – user253751 Dec 14 '16 at 00:20
  • But a class can have static object of self type. – RajeshDA Apr 20 '20 at 06:30

7 Answers7

48

You're only declaring the variable and not creating it. Try creating it at declaration or in the constructor and let me know what happens:

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

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

Incidentally, if you don't create it in the class, but rather accept a reference to it in a getter method or a constructor parameter, your code will work just fine. This is how some linked lists work.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • 2
    Anyone who is seeking for a right answer should hava a look at [answer from @David Rodríguez - dribe](http://stackoverflow.com/questions/13607757/object-of-the-class-as-instance-variable-inside-the-class?lq=1#answer-13608203), which HovercraftFullOfEels himself upvotes. – duleshi Jul 25 '13 at 09:03
  • 1
    Though, you could instantiate a static member variable of class Abc in class Abc's definition, using a static initializer. – Jayz7522 Oct 19 '14 at 01:19
  • @Hovercraft Full Of Eels: great example of Linked Lists. It makes sense now that i know why someone would declare a class as described by the OP. – gnsb Jan 14 '16 at 12:13
  • How about private static final Abc p = new Abc(); inside Abc class? @HovercraftFullOfEels – Norutan Apr 06 '17 at 16:09
  • 1
    @TanTran Interesting question. I get the same error, but I don't think the behavior is part of the specifications, so it probably depends on the implementation. More importantly, while we can ask these kind of questions (what will happen if we write X?), we need to first be clear about we really *mean* or *want* to achieve (in terms of memory, instructions, etc.) The language is just a contract with the compiler to express what we want. – flow2k Aug 20 '17 at 08:59
24

The difference lies in compile-time vs run-time checks.

In the first case (compile time), you are declaring that you will have a reference to a value of type Abc in this instance. The compiler will be aware of this when it checks for proper semantics, and since it knows the type upon compile time, it sees no issue with this.

In the second case (run time), you will actually create a value for this reference to refer to. This is where you could potentially get yourself into trouble. For example, if you said the following:

public class Abc {
    private Abc p;

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

This could lead you into trouble for the exact reason you cited (recursion that contains no base case and will continually allocate memory until you've run the VM out of heap space).

However, you can still do something similar to this and avoid the infinite recursion. By avoiding creating the value during construction, you put it off until a method is called for. In fact, it's one of the common ways to implement the singleton pattern in Java. For example:

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

This is perfectly valid because you only create one new instance of the value, and since run-time will have loaded the class already, it will know the type of the instance variable is valid.

Pshemo
  • 122,468
  • 25
  • 185
  • 269
pseudoramble
  • 2,541
  • 22
  • 28
13

When you want to model some real-world scenarios, you might have to use this notion. For example, think of a Tree's branch. A tree's branch might have n number of branches on it. Or from computer science background, think of a linked list's Node. A node will have reference to the node next to it. At the end, the next will contain a null to indicate end of the list.

So, this is only a reference, indicating that this type might refer to one of it's own. Nothing more.

ring bearer
  • 20,383
  • 7
  • 59
  • 72
2

As the chosen answer states, it's okay because the variable is not being instantiated, but rather it is accepting a reference from a set method or constructor argument. So you see, it's just a reference, just an address.

However though, there is a way of instantiating it without causing the nightmare loop, and that's by declaring it as static. This sort of bends the recursion rule because a static member is not on an object level but on a class level.

So...

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

Works just fine because the variable 'p' is not really affected by the instantiation of the Abc class. It's almost the equivalent of creating the 'p' object in another class.

Remember that I can do this...

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

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

Without creating a new object of Abc in the main method. That's how statics work, they're virtually unaffected by instantion of objects, and in the case of your question, they are the exception to the recursion rule.

Pshemo
  • 122,468
  • 25
  • 185
  • 269
WTRIX
  • 95
  • 1
  • 8
2

The basic use of this may be the Linked list in the data structure. It is a core concept in the Linked list, a reference from one node to the another node. The class Node consists of the basic element for Linked list.

class Node{
    private int value;
    private Node next; //reference, as a pointer to another node, →

    Node(){
        this.value=0;
        this.next=null;
    }
    Node(int value){
        this.value=value;
        this.next=null;
    }
    Node(int value,Node next){
        this.value=value;
        this.next=next;
    }
    public int getValue() {
        return this.value;
    }
    public void setValue(int value) {
        this.value=value;
    }
    public Node getNext() {
        return this.next;
    }
    public void setNext(Node next) {
        this.next=next;
    }
}

And we can connect these node by using the reference.

class Linkedlist{
    Node head = new Node();
    Node one = new Node();
    Node two = new Node();

    // Assign data values 
    one.value = 1;
    two.value = 2;

    // Connect nodes 
    one.next = two;
    two.next = NULL;

    //Save address of first node in head
    head = one;
}

Head → 1 next → 2 next → NULL

Therefore, this is only a reference type connecting one node to the another node in the data structure of the linked list.

LEO CHEN
  • 23
  • 4
1

Summarizing some of the answers here.

The class contains reference to one of its own kind.The best analogy in real world would be of a treasure hunt. A Clue place has some data and a clue in it which leads to another clue place which again you know repeats.

It is not saying that a clue place has another clue place in it, the kind of which you are seeming to infer.

Rakesh
  • 104
  • 1
  • 11
0

While we are talking about "recursion", the "execution" of code is the point we are considering. Yes, it is "dynamic" if a "recursion" is occurring.

In type declaration, a variable that contains a member, which is the type of containing variable, is "static". For example, a box may contain another box inside it, and so on. But there is finally a smallest box which doesn't contain anything. For a chain of railway carriages, every carriage has a member to next carriage except the last carriage.

In program, the static data must be "finite"(You don't have "infinite" space of memory). The execution of code may be "infinite". But there is an exception. Just imaging a circle of chain.

Mike Lue
  • 839
  • 4
  • 8