I was reading Java 8 collection interface documentation. I noticed Java 8 Collection Interface added this paragraph in the description which is not included in Java 7 Collection Interface .
Some collection operations which perform recursive traversal of the collection may fail with an exception for self-referential instances where the collection directly or indirectly contains itself. This includes the clone(), equals(), hashCode() and toString() methods. Implementations may optionally handle the self-referential scenario, however most current implementations do not do so.
I am a little confused as of why this paragraph is included. Is it because Java 7 cannot have self-referential instances where collection directly or indirectly contains itself? Then Java 8 introduced new interface or some new feature that allows that?
I am looking for detail explanations and it will be great if you includes an example to illustrate your point.
The toString()
format for List
is specified; it must recursively toString()
the contents and render them in a comma-separated list delimited by [ ... ]
. If you create a List
as follows:
List<Object> list = new ArrayList<>();
list.add(list);
System.out.println(list);
a naive implementation of toString()
would throw StackOverflowError
(try it.) The Collection
implementations try to defend against this problem for some core methods, in some cases; that's what this paragraph is saying.
Is it because Java7 cannot have self-referential instances where collection directly or indirectly contains itself? Then Java8 introduced new interface or some new feature that allows that?
I don't think so.
Before Java 8, instances may of course have self reference.
And using them in a Collection
may create of course infinite loops and failure at runtime with a StackOverflowError
thrown.
Here two classes where instance fields have circular dependencies between them and which the toString()
method of each class relies on their own fields.
Parent that refers to Child :
public class Parent {
private List<Child> childs;
public Parent(List<Child> childs) {
this.childs = childs;
}
@Override
public String toString() {
return "Parent [childs=" + childs + "]";
}
}
Child that refers to Parent :
public class Child {
private Parent parent;
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
@Override
public String toString() {
return "Child [parent=" + parent + "]";
}
}
Suppose now you create a Child and a associated Parent :
List<Child> childs = new ArrayList<>();
Child child = new Child();
childs.add(child);
Parent parent = new Parent(childs);
child.setParent(parent);
Now you can invoke :
parent.toString();
child.toString();
or on a Collection
instance such as :
childs.toString();
You will get exactly the same result : java.lang.StackOverflowError
as the child invokes the parent that invokes the child that invokes the parent and so on...
The doc was very probably updated with Java 8 to enforce the risk of implementing these methods in a brittle way as the Collection
implementations generally don't address it and it makes sense as hiding the malfunctioning of a bugged client code has to be avoided otherwise the problem will never be solved.
"Implementations may optionally handle the self-referential scenario, however most current implementations do not do so."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With