3

I have a class that looks something like this:

public class Node {
    private final Node otherNode;
    public Node(Node otherNode) {
        this.otherNode = otherNode;
    }
}

and want to do something like

Node n1, n2 ;
n1 = new Node(n2);
n2 = new Node(n1);

but obviously cannot since n2 is not initialized yet. I don't want to use a setter to set otherNode because it's final, and thus should only be set once ever. What is the cleanest approach to accomplishing this? Is there some fancy Java syntax I'm unfamiliar with to let me do this? Should I use an initialize method in addition to the constructor (ugly), or just cave and use a setter (also ugly)?

user1174528
  • 125
  • 2
  • 5
  • Only way I know is with a setter. Who came first? The chicken or the egg? – Sotirios Delimanolis May 23 '13 at 23:53
  • Private constructor and if otherNode == null call private constructor? – arynaq May 23 '13 at 23:54
  • 1
    Technically you can achieve this with reflection, but I don't recommend it. – ruakh May 23 '13 at 23:54
  • @ruakh How would you do it with reflection, that is different from using a setter? – Sotirios Delimanolis May 23 '13 at 23:55
  • @SotiriosDelimanolis: I'm not sure what you mean by "different from using a setter", but what I meant was, you can write something like `final Node firstNode = new Node(null); final Node secondNode = new Node(firstNode); final java.lang.reflect.Field otherNodeField = Node.class.getField("otherNode"); otherNodeField.setAccessible(true); otherNodeField.set(firstNode, secondNode);`. (Oddly, the Javadoc for `setAccessible` doesn't mention that it has this effect -- it only mentions its relationship to `private`/`protected`/[default]/`public` -- but you can try it and see.) – ruakh May 24 '13 at 00:07
  • What I mean is that the steps are the same or slightly worse than using a setter. – Sotirios Delimanolis May 24 '13 at 00:10
  • (The reason for this behavior, by the way, is to support use cases like general-purpose deserialization/marshalling/etc. frameworks. In versions of Java before `setAccessible` affected `final`-ness, such frameworks couldn't handle `final` fields.) – ruakh May 24 '13 at 00:10
  • @SotiriosDelimanolis: I assume that the reason the OP doesn't want to use a setter isn't because "the steps are [bad]", but rather, because (s)he wants the fields to remain `final`. – ruakh May 24 '13 at 00:12
  • @ruakh Right. With reflection you'll need to do a little bit more than what you showed, `setAccessible()` is not enough to re-set a final field. http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection – Sotirios Delimanolis May 24 '13 at 00:18
  • @SotiriosDelimanolis: No: I tried this technique before posting it. I may have a typo in the version here, but nothing major is missing. In particular, I certainly did *not* have to mess directly with the modifiers. – ruakh May 24 '13 at 02:07

2 Answers2

9

Have a second constructor that takes no parameters and constructs its own Node, passing itself as the other's "other".

public class Node
{
   private final Node otherNode;

   public Node(Node other)
   {
      otherNode = other;
   }

   public Node()
   {
      otherNode = new Node(this);
   }

   public Node getOther()
   {
      return otherNode;
   }
}

Then when using it:

Node n1 = new Node();
Node n2 = n1.getOther();

Assuring that they refer to each other:

System.out.println(n1 == n1.getOther().getOther());
System.out.println(n2 == n2.getOther().getOther());
System.out.println(n1 == n2.getOther());
System.out.println(n2 == n1.getOther());

These all print true.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • This sounds perfect! Thanks – user1174528 May 24 '13 at 00:00
  • 1
    While this approach works, it doesn't seem very "obvious" to a user. I'd prefer an explicit static method createNodeAndOther(). At a minimum, this constructor needs some good javadocs. – user949300 May 24 '13 at 00:03
  • @user949300: I agree; if you post an answer that demonstrates how to create such a method, you'll get a +1 from me. – ruakh May 24 '13 at 00:13
  • @ruakh Upon brief further review, I don't think it can be done in a purely static method. You need some combination of a "special constructor" and a static method, like in your answer. – user949300 May 27 '13 at 15:09
2

(This is a supplement to rgettman's answer.)

A more general solution is to write a constructor like:

private Node(final int numNodesInLoop) {
    if(numNodesInLoop < 1) {
        throw new IllegalArgumentException();
    }
    Node head = this;
    for(int i = 1; i < numNodesInLoop) {
        head = new Node(head);
    }
    this.otherNode = head;
}

Your case, with two nodes, would be instantiated as new Node(2).

I made the above private, per a comment by user949300 to rgettman's answer, because the meaning of a Node constructor that takes an int is not very guessable (it creates a loop?!), so it's better to wrap it in a static factory method whose name makes its functionality clear:

public static Node newNodeLoop(final int numNodes) {
    return new Node(numNodes);
}

(This is also more future-proof in case you later have a need for another constructor that would take an int, for whatever reason. You can then modify this constructor to take a dummy argument as well, just enough to tell the compiler what constructor you want. The factory method would still have the same contract.)

ruakh
  • 175,680
  • 26
  • 273
  • 307