Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 vs Java 7 Collection Interface: self-referential instance

Tags:

java-8

java-7

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.

like image 794
OLIVER.KOO Avatar asked Aug 17 '17 19:08

OLIVER.KOO


2 Answers

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.

like image 116
Brian Goetz Avatar answered Oct 22 '22 03:10

Brian Goetz


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."

like image 33
davidxxx Avatar answered Oct 22 '22 01:10

davidxxx