Java generics self-reference: is it safe?











up vote
11
down vote

favorite
1












I have this simple interface:



public interface Node<E extends Node<E>>
{
public E getParent();

public List<E> getChildren();

default List<E> listNodes()
{
List<E> result = new ArrayList<>();

// ------> is this always safe? <-----
@SuppressWarnings("unchecked")
E root = (E) this;

Queue<E> queue = new ArrayDeque<>();
queue.add(root);

while(!queue.isEmpty())
{
E node = queue.remove();

result.add(node);

queue.addAll(node.getChildren());
}

return result;
}
}


I see that this is always an instance of Node<E> (by definition).

But I can't imagine a case where this is not an instance of E...

Since E extends Node<E>, shouldn't Node<E> also be equivalent to E by definition??



Can you give an example of an object that's an instance of Node<E>, but it's not an instance of E??



Meanwhile, my brain is melting...





The previous class was a simplified example.

To show why I need a self-bound, I'm adding a bit of complexity:



public interface Node<E extends Node<E, R>, R extends NodeRelation<E>>
{
public List<R> getParents();

public List<R> getChildren();

default List<E> listDescendants()
{
List<E> result = new ArrayList<>();

@SuppressWarnings("unchecked")
E root = (E) this;

Queue<E> queue = new ArrayDeque<>();
queue.add(root);

while(!queue.isEmpty())
{
E node = queue.remove();

result.add(node);

node.getChildren()
.stream()
.map(NodeRelation::getChild)
.forEach(queue::add);
}

return result;
}
}

public interface NodeRelation<E>
{
public E getParent();

public E getChild();
}









share|improve this question




























    up vote
    11
    down vote

    favorite
    1












    I have this simple interface:



    public interface Node<E extends Node<E>>
    {
    public E getParent();

    public List<E> getChildren();

    default List<E> listNodes()
    {
    List<E> result = new ArrayList<>();

    // ------> is this always safe? <-----
    @SuppressWarnings("unchecked")
    E root = (E) this;

    Queue<E> queue = new ArrayDeque<>();
    queue.add(root);

    while(!queue.isEmpty())
    {
    E node = queue.remove();

    result.add(node);

    queue.addAll(node.getChildren());
    }

    return result;
    }
    }


    I see that this is always an instance of Node<E> (by definition).

    But I can't imagine a case where this is not an instance of E...

    Since E extends Node<E>, shouldn't Node<E> also be equivalent to E by definition??



    Can you give an example of an object that's an instance of Node<E>, but it's not an instance of E??



    Meanwhile, my brain is melting...





    The previous class was a simplified example.

    To show why I need a self-bound, I'm adding a bit of complexity:



    public interface Node<E extends Node<E, R>, R extends NodeRelation<E>>
    {
    public List<R> getParents();

    public List<R> getChildren();

    default List<E> listDescendants()
    {
    List<E> result = new ArrayList<>();

    @SuppressWarnings("unchecked")
    E root = (E) this;

    Queue<E> queue = new ArrayDeque<>();
    queue.add(root);

    while(!queue.isEmpty())
    {
    E node = queue.remove();

    result.add(node);

    node.getChildren()
    .stream()
    .map(NodeRelation::getChild)
    .forEach(queue::add);
    }

    return result;
    }
    }

    public interface NodeRelation<E>
    {
    public E getParent();

    public E getChild();
    }









    share|improve this question


























      up vote
      11
      down vote

      favorite
      1









      up vote
      11
      down vote

      favorite
      1






      1





      I have this simple interface:



      public interface Node<E extends Node<E>>
      {
      public E getParent();

      public List<E> getChildren();

      default List<E> listNodes()
      {
      List<E> result = new ArrayList<>();

      // ------> is this always safe? <-----
      @SuppressWarnings("unchecked")
      E root = (E) this;

      Queue<E> queue = new ArrayDeque<>();
      queue.add(root);

      while(!queue.isEmpty())
      {
      E node = queue.remove();

      result.add(node);

      queue.addAll(node.getChildren());
      }

      return result;
      }
      }


      I see that this is always an instance of Node<E> (by definition).

      But I can't imagine a case where this is not an instance of E...

      Since E extends Node<E>, shouldn't Node<E> also be equivalent to E by definition??



      Can you give an example of an object that's an instance of Node<E>, but it's not an instance of E??



      Meanwhile, my brain is melting...





      The previous class was a simplified example.

      To show why I need a self-bound, I'm adding a bit of complexity:



      public interface Node<E extends Node<E, R>, R extends NodeRelation<E>>
      {
      public List<R> getParents();

      public List<R> getChildren();

      default List<E> listDescendants()
      {
      List<E> result = new ArrayList<>();

      @SuppressWarnings("unchecked")
      E root = (E) this;

      Queue<E> queue = new ArrayDeque<>();
      queue.add(root);

      while(!queue.isEmpty())
      {
      E node = queue.remove();

      result.add(node);

      node.getChildren()
      .stream()
      .map(NodeRelation::getChild)
      .forEach(queue::add);
      }

      return result;
      }
      }

      public interface NodeRelation<E>
      {
      public E getParent();

      public E getChild();
      }









      share|improve this question















      I have this simple interface:



      public interface Node<E extends Node<E>>
      {
      public E getParent();

      public List<E> getChildren();

      default List<E> listNodes()
      {
      List<E> result = new ArrayList<>();

      // ------> is this always safe? <-----
      @SuppressWarnings("unchecked")
      E root = (E) this;

      Queue<E> queue = new ArrayDeque<>();
      queue.add(root);

      while(!queue.isEmpty())
      {
      E node = queue.remove();

      result.add(node);

      queue.addAll(node.getChildren());
      }

      return result;
      }
      }


      I see that this is always an instance of Node<E> (by definition).

      But I can't imagine a case where this is not an instance of E...

      Since E extends Node<E>, shouldn't Node<E> also be equivalent to E by definition??



      Can you give an example of an object that's an instance of Node<E>, but it's not an instance of E??



      Meanwhile, my brain is melting...





      The previous class was a simplified example.

      To show why I need a self-bound, I'm adding a bit of complexity:



      public interface Node<E extends Node<E, R>, R extends NodeRelation<E>>
      {
      public List<R> getParents();

      public List<R> getChildren();

      default List<E> listDescendants()
      {
      List<E> result = new ArrayList<>();

      @SuppressWarnings("unchecked")
      E root = (E) this;

      Queue<E> queue = new ArrayDeque<>();
      queue.add(root);

      while(!queue.isEmpty())
      {
      E node = queue.remove();

      result.add(node);

      node.getChildren()
      .stream()
      .map(NodeRelation::getChild)
      .forEach(queue::add);
      }

      return result;
      }
      }

      public interface NodeRelation<E>
      {
      public E getParent();

      public E getChild();
      }






      java generics this self-reference






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 10 hours ago

























      asked 10 hours ago









      Michele Mariotti

      5,65543163




      5,65543163
























          4 Answers
          4






          active

          oldest

          votes

















          up vote
          7
          down vote



          accepted










          An easy example to illustrate the problem: a node of a different type of node:



          class NodeA implements Node<NodeA> {
          ...
          }


          And:



          class NodeB implements Node<NodeA> {
          ...
          }


          In this case, E root = (E) this would resolve to NodeA root = (NodeA) this, where this is a NodeB. And that's incompatible.






          share|improve this answer





















          • That's it! Thanks!
            – Michele Mariotti
            10 hours ago






          • 2




            I'm not sure that's correct. I was going to post something similar, but then I tested it, and E root = (E) this; doesn't throw an exception. E root = (E) this; is not resolved to NodeA root = (NodeA) this due to type erasure. If I'm not mistaken, it can only be resolved to Node root = (Node) this; @MicheleMariotti
            – Eran
            10 hours ago












          • @Eran I think ClassCastException may happens when you iterate through result of 'listDescendants()'
            – Aleksandr Semyannikov
            9 hours ago












          • @Eran I'm very curious to know why it's not failing at that point. I know for sure that the types are incompatible, but I have to find an accurate explanation of why the CCE is not thrown on that line (could be that Java is using Object for erasure, but I don't know). Will comment when I find an exact reason.
            – ernest_k
            9 hours ago






          • 2




            @ernest_k I've just seen a bytecode, you were right about type erasure, 'E root = (E) this;' - actually that cast will be ignored by compiler. It casts objects only when generic is resolved by particular parameter, like when we are iterating a result list.
            – Aleksandr Semyannikov
            9 hours ago


















          up vote
          2
          down vote













          Without <E extends Node<E>>, you could have either of these cases:



          Node<Integer>


          where the generic type isn't a Node at all, or



          Node<DifferentNode>


          where the generic bounds don't match.



          That said, it's not typical to see a bound this way, as Node<E> is expected to be a node that contains some value of type E, and children would be a List<Node<E>>, not a List<E>.






          share|improve this answer





















          • I know it's not typical, but I have a complex case where self-bound is required. Please, see the update on the question. Thanks :)
            – Michele Mariotti
            10 hours ago






          • 1




            Yeah, very weird to see "curiously recursive template pattern" used for a graph structure.
            – flakes
            9 hours ago












          • Not weird at all for something like IComparable in C# example
            – D. Ben Knoble
            1 hour ago


















          up vote
          2
          down vote













          The problem is not in E root = (E) this. It might work well until you start iterating through result of listNodes().



          That example demonstrates where exactly ClassCastException will be thrown:



          public interface Node<E extends Node<E>> {

          List<E> getRelatedNodes();

          default List<E> getAllNodes() {
          List<E> result = new ArrayList<>();
          result.add((E) this); //<--that cast is not a problem because of type erasure
          return result;
          }
          }

          class NodeA implements Node<NodeA> {

          public NodeA() {
          }

          @Override
          public List<NodeA> getRelatedNodes() {
          return null;
          }
          }

          class NodeB implements Node<NodeA> {

          private List<NodeA> relatedNodes;

          public NodeB(List<NodeA> relatedNodes) {
          this.relatedNodes = relatedNodes;
          }

          @Override
          public List<NodeA> getRelatedNodes() {
          return relatedNodes;
          }
          }


          Execute:



          List<NodeA> nodes = new NodeB(Arrays.asList(new NodeA())).getAllNodes(); //according to generic it is list of NodeA objects
          for (NodeA node : nodes) { //ClassCastException will be thrown
          System.out.println(node);
          }





          share|improve this answer




























            up vote
            0
            down vote













            With this sort of situation it is often useful to have a getThis method that (by convention) returns this.



            I would do the following



            public interface Node<E extends Node<E, R>,
            R extends NodeRelation<E, R>>
            {
            public List<R> getParents();
            public List<R> getChildren();
            public List<E> listDescendants() ;
            }
            public interface NodeRelation<E extends Node<E, R>,
            R extends NodeRelation<E, R>>
            {
            public E getParent();
            public E getChild();
            }
            abstract class ANode<E extends ANode<E,R>,
            R extends ARelation<E,R>>
            implements Node<E,R> {
            abstract protected E getThis() ;
            public List<E> listDescendants()
            {
            List<E> result = new ArrayList<>();
            E root = getThis() ;
            ...
            return result;
            }

            }

            abstract class ARelation<E extends ANode<E,R>,
            R extends ARelation<E,R>>
            implements NodeRelation<E,R> {
            }

            class CNode extends ANode<CNode, CRelation> {
            public CNode getThis() { return this ; }
            ...
            }

            class CRelation extends ARelation<CNode, CRelation> {
            ...
            }


            Although I might not bother with having both abstract class and interface layers.






            share|improve this answer























              Your Answer






              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "1"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              convertImagesToLinks: true,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: 10,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














               

              draft saved


              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53400009%2fjava-generics-self-reference-is-it-safe%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              4 Answers
              4






              active

              oldest

              votes








              4 Answers
              4






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes








              up vote
              7
              down vote



              accepted










              An easy example to illustrate the problem: a node of a different type of node:



              class NodeA implements Node<NodeA> {
              ...
              }


              And:



              class NodeB implements Node<NodeA> {
              ...
              }


              In this case, E root = (E) this would resolve to NodeA root = (NodeA) this, where this is a NodeB. And that's incompatible.






              share|improve this answer





















              • That's it! Thanks!
                – Michele Mariotti
                10 hours ago






              • 2




                I'm not sure that's correct. I was going to post something similar, but then I tested it, and E root = (E) this; doesn't throw an exception. E root = (E) this; is not resolved to NodeA root = (NodeA) this due to type erasure. If I'm not mistaken, it can only be resolved to Node root = (Node) this; @MicheleMariotti
                – Eran
                10 hours ago












              • @Eran I think ClassCastException may happens when you iterate through result of 'listDescendants()'
                – Aleksandr Semyannikov
                9 hours ago












              • @Eran I'm very curious to know why it's not failing at that point. I know for sure that the types are incompatible, but I have to find an accurate explanation of why the CCE is not thrown on that line (could be that Java is using Object for erasure, but I don't know). Will comment when I find an exact reason.
                – ernest_k
                9 hours ago






              • 2




                @ernest_k I've just seen a bytecode, you were right about type erasure, 'E root = (E) this;' - actually that cast will be ignored by compiler. It casts objects only when generic is resolved by particular parameter, like when we are iterating a result list.
                – Aleksandr Semyannikov
                9 hours ago















              up vote
              7
              down vote



              accepted










              An easy example to illustrate the problem: a node of a different type of node:



              class NodeA implements Node<NodeA> {
              ...
              }


              And:



              class NodeB implements Node<NodeA> {
              ...
              }


              In this case, E root = (E) this would resolve to NodeA root = (NodeA) this, where this is a NodeB. And that's incompatible.






              share|improve this answer





















              • That's it! Thanks!
                – Michele Mariotti
                10 hours ago






              • 2




                I'm not sure that's correct. I was going to post something similar, but then I tested it, and E root = (E) this; doesn't throw an exception. E root = (E) this; is not resolved to NodeA root = (NodeA) this due to type erasure. If I'm not mistaken, it can only be resolved to Node root = (Node) this; @MicheleMariotti
                – Eran
                10 hours ago












              • @Eran I think ClassCastException may happens when you iterate through result of 'listDescendants()'
                – Aleksandr Semyannikov
                9 hours ago












              • @Eran I'm very curious to know why it's not failing at that point. I know for sure that the types are incompatible, but I have to find an accurate explanation of why the CCE is not thrown on that line (could be that Java is using Object for erasure, but I don't know). Will comment when I find an exact reason.
                – ernest_k
                9 hours ago






              • 2




                @ernest_k I've just seen a bytecode, you were right about type erasure, 'E root = (E) this;' - actually that cast will be ignored by compiler. It casts objects only when generic is resolved by particular parameter, like when we are iterating a result list.
                – Aleksandr Semyannikov
                9 hours ago













              up vote
              7
              down vote



              accepted







              up vote
              7
              down vote



              accepted






              An easy example to illustrate the problem: a node of a different type of node:



              class NodeA implements Node<NodeA> {
              ...
              }


              And:



              class NodeB implements Node<NodeA> {
              ...
              }


              In this case, E root = (E) this would resolve to NodeA root = (NodeA) this, where this is a NodeB. And that's incompatible.






              share|improve this answer












              An easy example to illustrate the problem: a node of a different type of node:



              class NodeA implements Node<NodeA> {
              ...
              }


              And:



              class NodeB implements Node<NodeA> {
              ...
              }


              In this case, E root = (E) this would resolve to NodeA root = (NodeA) this, where this is a NodeB. And that's incompatible.







              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered 10 hours ago









              ernest_k

              17.8k41836




              17.8k41836












              • That's it! Thanks!
                – Michele Mariotti
                10 hours ago






              • 2




                I'm not sure that's correct. I was going to post something similar, but then I tested it, and E root = (E) this; doesn't throw an exception. E root = (E) this; is not resolved to NodeA root = (NodeA) this due to type erasure. If I'm not mistaken, it can only be resolved to Node root = (Node) this; @MicheleMariotti
                – Eran
                10 hours ago












              • @Eran I think ClassCastException may happens when you iterate through result of 'listDescendants()'
                – Aleksandr Semyannikov
                9 hours ago












              • @Eran I'm very curious to know why it's not failing at that point. I know for sure that the types are incompatible, but I have to find an accurate explanation of why the CCE is not thrown on that line (could be that Java is using Object for erasure, but I don't know). Will comment when I find an exact reason.
                – ernest_k
                9 hours ago






              • 2




                @ernest_k I've just seen a bytecode, you were right about type erasure, 'E root = (E) this;' - actually that cast will be ignored by compiler. It casts objects only when generic is resolved by particular parameter, like when we are iterating a result list.
                – Aleksandr Semyannikov
                9 hours ago


















              • That's it! Thanks!
                – Michele Mariotti
                10 hours ago






              • 2




                I'm not sure that's correct. I was going to post something similar, but then I tested it, and E root = (E) this; doesn't throw an exception. E root = (E) this; is not resolved to NodeA root = (NodeA) this due to type erasure. If I'm not mistaken, it can only be resolved to Node root = (Node) this; @MicheleMariotti
                – Eran
                10 hours ago












              • @Eran I think ClassCastException may happens when you iterate through result of 'listDescendants()'
                – Aleksandr Semyannikov
                9 hours ago












              • @Eran I'm very curious to know why it's not failing at that point. I know for sure that the types are incompatible, but I have to find an accurate explanation of why the CCE is not thrown on that line (could be that Java is using Object for erasure, but I don't know). Will comment when I find an exact reason.
                – ernest_k
                9 hours ago






              • 2




                @ernest_k I've just seen a bytecode, you were right about type erasure, 'E root = (E) this;' - actually that cast will be ignored by compiler. It casts objects only when generic is resolved by particular parameter, like when we are iterating a result list.
                – Aleksandr Semyannikov
                9 hours ago
















              That's it! Thanks!
              – Michele Mariotti
              10 hours ago




              That's it! Thanks!
              – Michele Mariotti
              10 hours ago




              2




              2




              I'm not sure that's correct. I was going to post something similar, but then I tested it, and E root = (E) this; doesn't throw an exception. E root = (E) this; is not resolved to NodeA root = (NodeA) this due to type erasure. If I'm not mistaken, it can only be resolved to Node root = (Node) this; @MicheleMariotti
              – Eran
              10 hours ago






              I'm not sure that's correct. I was going to post something similar, but then I tested it, and E root = (E) this; doesn't throw an exception. E root = (E) this; is not resolved to NodeA root = (NodeA) this due to type erasure. If I'm not mistaken, it can only be resolved to Node root = (Node) this; @MicheleMariotti
              – Eran
              10 hours ago














              @Eran I think ClassCastException may happens when you iterate through result of 'listDescendants()'
              – Aleksandr Semyannikov
              9 hours ago






              @Eran I think ClassCastException may happens when you iterate through result of 'listDescendants()'
              – Aleksandr Semyannikov
              9 hours ago














              @Eran I'm very curious to know why it's not failing at that point. I know for sure that the types are incompatible, but I have to find an accurate explanation of why the CCE is not thrown on that line (could be that Java is using Object for erasure, but I don't know). Will comment when I find an exact reason.
              – ernest_k
              9 hours ago




              @Eran I'm very curious to know why it's not failing at that point. I know for sure that the types are incompatible, but I have to find an accurate explanation of why the CCE is not thrown on that line (could be that Java is using Object for erasure, but I don't know). Will comment when I find an exact reason.
              – ernest_k
              9 hours ago




              2




              2




              @ernest_k I've just seen a bytecode, you were right about type erasure, 'E root = (E) this;' - actually that cast will be ignored by compiler. It casts objects only when generic is resolved by particular parameter, like when we are iterating a result list.
              – Aleksandr Semyannikov
              9 hours ago




              @ernest_k I've just seen a bytecode, you were right about type erasure, 'E root = (E) this;' - actually that cast will be ignored by compiler. It casts objects only when generic is resolved by particular parameter, like when we are iterating a result list.
              – Aleksandr Semyannikov
              9 hours ago












              up vote
              2
              down vote













              Without <E extends Node<E>>, you could have either of these cases:



              Node<Integer>


              where the generic type isn't a Node at all, or



              Node<DifferentNode>


              where the generic bounds don't match.



              That said, it's not typical to see a bound this way, as Node<E> is expected to be a node that contains some value of type E, and children would be a List<Node<E>>, not a List<E>.






              share|improve this answer





















              • I know it's not typical, but I have a complex case where self-bound is required. Please, see the update on the question. Thanks :)
                – Michele Mariotti
                10 hours ago






              • 1




                Yeah, very weird to see "curiously recursive template pattern" used for a graph structure.
                – flakes
                9 hours ago












              • Not weird at all for something like IComparable in C# example
                – D. Ben Knoble
                1 hour ago















              up vote
              2
              down vote













              Without <E extends Node<E>>, you could have either of these cases:



              Node<Integer>


              where the generic type isn't a Node at all, or



              Node<DifferentNode>


              where the generic bounds don't match.



              That said, it's not typical to see a bound this way, as Node<E> is expected to be a node that contains some value of type E, and children would be a List<Node<E>>, not a List<E>.






              share|improve this answer





















              • I know it's not typical, but I have a complex case where self-bound is required. Please, see the update on the question. Thanks :)
                – Michele Mariotti
                10 hours ago






              • 1




                Yeah, very weird to see "curiously recursive template pattern" used for a graph structure.
                – flakes
                9 hours ago












              • Not weird at all for something like IComparable in C# example
                – D. Ben Knoble
                1 hour ago













              up vote
              2
              down vote










              up vote
              2
              down vote









              Without <E extends Node<E>>, you could have either of these cases:



              Node<Integer>


              where the generic type isn't a Node at all, or



              Node<DifferentNode>


              where the generic bounds don't match.



              That said, it's not typical to see a bound this way, as Node<E> is expected to be a node that contains some value of type E, and children would be a List<Node<E>>, not a List<E>.






              share|improve this answer












              Without <E extends Node<E>>, you could have either of these cases:



              Node<Integer>


              where the generic type isn't a Node at all, or



              Node<DifferentNode>


              where the generic bounds don't match.



              That said, it's not typical to see a bound this way, as Node<E> is expected to be a node that contains some value of type E, and children would be a List<Node<E>>, not a List<E>.







              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered 10 hours ago









              chrylis

              49.8k1678114




              49.8k1678114












              • I know it's not typical, but I have a complex case where self-bound is required. Please, see the update on the question. Thanks :)
                – Michele Mariotti
                10 hours ago






              • 1




                Yeah, very weird to see "curiously recursive template pattern" used for a graph structure.
                – flakes
                9 hours ago












              • Not weird at all for something like IComparable in C# example
                – D. Ben Knoble
                1 hour ago


















              • I know it's not typical, but I have a complex case where self-bound is required. Please, see the update on the question. Thanks :)
                – Michele Mariotti
                10 hours ago






              • 1




                Yeah, very weird to see "curiously recursive template pattern" used for a graph structure.
                – flakes
                9 hours ago












              • Not weird at all for something like IComparable in C# example
                – D. Ben Knoble
                1 hour ago
















              I know it's not typical, but I have a complex case where self-bound is required. Please, see the update on the question. Thanks :)
              – Michele Mariotti
              10 hours ago




              I know it's not typical, but I have a complex case where self-bound is required. Please, see the update on the question. Thanks :)
              – Michele Mariotti
              10 hours ago




              1




              1




              Yeah, very weird to see "curiously recursive template pattern" used for a graph structure.
              – flakes
              9 hours ago






              Yeah, very weird to see "curiously recursive template pattern" used for a graph structure.
              – flakes
              9 hours ago














              Not weird at all for something like IComparable in C# example
              – D. Ben Knoble
              1 hour ago




              Not weird at all for something like IComparable in C# example
              – D. Ben Knoble
              1 hour ago










              up vote
              2
              down vote













              The problem is not in E root = (E) this. It might work well until you start iterating through result of listNodes().



              That example demonstrates where exactly ClassCastException will be thrown:



              public interface Node<E extends Node<E>> {

              List<E> getRelatedNodes();

              default List<E> getAllNodes() {
              List<E> result = new ArrayList<>();
              result.add((E) this); //<--that cast is not a problem because of type erasure
              return result;
              }
              }

              class NodeA implements Node<NodeA> {

              public NodeA() {
              }

              @Override
              public List<NodeA> getRelatedNodes() {
              return null;
              }
              }

              class NodeB implements Node<NodeA> {

              private List<NodeA> relatedNodes;

              public NodeB(List<NodeA> relatedNodes) {
              this.relatedNodes = relatedNodes;
              }

              @Override
              public List<NodeA> getRelatedNodes() {
              return relatedNodes;
              }
              }


              Execute:



              List<NodeA> nodes = new NodeB(Arrays.asList(new NodeA())).getAllNodes(); //according to generic it is list of NodeA objects
              for (NodeA node : nodes) { //ClassCastException will be thrown
              System.out.println(node);
              }





              share|improve this answer

























                up vote
                2
                down vote













                The problem is not in E root = (E) this. It might work well until you start iterating through result of listNodes().



                That example demonstrates where exactly ClassCastException will be thrown:



                public interface Node<E extends Node<E>> {

                List<E> getRelatedNodes();

                default List<E> getAllNodes() {
                List<E> result = new ArrayList<>();
                result.add((E) this); //<--that cast is not a problem because of type erasure
                return result;
                }
                }

                class NodeA implements Node<NodeA> {

                public NodeA() {
                }

                @Override
                public List<NodeA> getRelatedNodes() {
                return null;
                }
                }

                class NodeB implements Node<NodeA> {

                private List<NodeA> relatedNodes;

                public NodeB(List<NodeA> relatedNodes) {
                this.relatedNodes = relatedNodes;
                }

                @Override
                public List<NodeA> getRelatedNodes() {
                return relatedNodes;
                }
                }


                Execute:



                List<NodeA> nodes = new NodeB(Arrays.asList(new NodeA())).getAllNodes(); //according to generic it is list of NodeA objects
                for (NodeA node : nodes) { //ClassCastException will be thrown
                System.out.println(node);
                }





                share|improve this answer























                  up vote
                  2
                  down vote










                  up vote
                  2
                  down vote









                  The problem is not in E root = (E) this. It might work well until you start iterating through result of listNodes().



                  That example demonstrates where exactly ClassCastException will be thrown:



                  public interface Node<E extends Node<E>> {

                  List<E> getRelatedNodes();

                  default List<E> getAllNodes() {
                  List<E> result = new ArrayList<>();
                  result.add((E) this); //<--that cast is not a problem because of type erasure
                  return result;
                  }
                  }

                  class NodeA implements Node<NodeA> {

                  public NodeA() {
                  }

                  @Override
                  public List<NodeA> getRelatedNodes() {
                  return null;
                  }
                  }

                  class NodeB implements Node<NodeA> {

                  private List<NodeA> relatedNodes;

                  public NodeB(List<NodeA> relatedNodes) {
                  this.relatedNodes = relatedNodes;
                  }

                  @Override
                  public List<NodeA> getRelatedNodes() {
                  return relatedNodes;
                  }
                  }


                  Execute:



                  List<NodeA> nodes = new NodeB(Arrays.asList(new NodeA())).getAllNodes(); //according to generic it is list of NodeA objects
                  for (NodeA node : nodes) { //ClassCastException will be thrown
                  System.out.println(node);
                  }





                  share|improve this answer












                  The problem is not in E root = (E) this. It might work well until you start iterating through result of listNodes().



                  That example demonstrates where exactly ClassCastException will be thrown:



                  public interface Node<E extends Node<E>> {

                  List<E> getRelatedNodes();

                  default List<E> getAllNodes() {
                  List<E> result = new ArrayList<>();
                  result.add((E) this); //<--that cast is not a problem because of type erasure
                  return result;
                  }
                  }

                  class NodeA implements Node<NodeA> {

                  public NodeA() {
                  }

                  @Override
                  public List<NodeA> getRelatedNodes() {
                  return null;
                  }
                  }

                  class NodeB implements Node<NodeA> {

                  private List<NodeA> relatedNodes;

                  public NodeB(List<NodeA> relatedNodes) {
                  this.relatedNodes = relatedNodes;
                  }

                  @Override
                  public List<NodeA> getRelatedNodes() {
                  return relatedNodes;
                  }
                  }


                  Execute:



                  List<NodeA> nodes = new NodeB(Arrays.asList(new NodeA())).getAllNodes(); //according to generic it is list of NodeA objects
                  for (NodeA node : nodes) { //ClassCastException will be thrown
                  System.out.println(node);
                  }






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 9 hours ago









                  Aleksandr Semyannikov

                  388112




                  388112






















                      up vote
                      0
                      down vote













                      With this sort of situation it is often useful to have a getThis method that (by convention) returns this.



                      I would do the following



                      public interface Node<E extends Node<E, R>,
                      R extends NodeRelation<E, R>>
                      {
                      public List<R> getParents();
                      public List<R> getChildren();
                      public List<E> listDescendants() ;
                      }
                      public interface NodeRelation<E extends Node<E, R>,
                      R extends NodeRelation<E, R>>
                      {
                      public E getParent();
                      public E getChild();
                      }
                      abstract class ANode<E extends ANode<E,R>,
                      R extends ARelation<E,R>>
                      implements Node<E,R> {
                      abstract protected E getThis() ;
                      public List<E> listDescendants()
                      {
                      List<E> result = new ArrayList<>();
                      E root = getThis() ;
                      ...
                      return result;
                      }

                      }

                      abstract class ARelation<E extends ANode<E,R>,
                      R extends ARelation<E,R>>
                      implements NodeRelation<E,R> {
                      }

                      class CNode extends ANode<CNode, CRelation> {
                      public CNode getThis() { return this ; }
                      ...
                      }

                      class CRelation extends ARelation<CNode, CRelation> {
                      ...
                      }


                      Although I might not bother with having both abstract class and interface layers.






                      share|improve this answer



























                        up vote
                        0
                        down vote













                        With this sort of situation it is often useful to have a getThis method that (by convention) returns this.



                        I would do the following



                        public interface Node<E extends Node<E, R>,
                        R extends NodeRelation<E, R>>
                        {
                        public List<R> getParents();
                        public List<R> getChildren();
                        public List<E> listDescendants() ;
                        }
                        public interface NodeRelation<E extends Node<E, R>,
                        R extends NodeRelation<E, R>>
                        {
                        public E getParent();
                        public E getChild();
                        }
                        abstract class ANode<E extends ANode<E,R>,
                        R extends ARelation<E,R>>
                        implements Node<E,R> {
                        abstract protected E getThis() ;
                        public List<E> listDescendants()
                        {
                        List<E> result = new ArrayList<>();
                        E root = getThis() ;
                        ...
                        return result;
                        }

                        }

                        abstract class ARelation<E extends ANode<E,R>,
                        R extends ARelation<E,R>>
                        implements NodeRelation<E,R> {
                        }

                        class CNode extends ANode<CNode, CRelation> {
                        public CNode getThis() { return this ; }
                        ...
                        }

                        class CRelation extends ARelation<CNode, CRelation> {
                        ...
                        }


                        Although I might not bother with having both abstract class and interface layers.






                        share|improve this answer

























                          up vote
                          0
                          down vote










                          up vote
                          0
                          down vote









                          With this sort of situation it is often useful to have a getThis method that (by convention) returns this.



                          I would do the following



                          public interface Node<E extends Node<E, R>,
                          R extends NodeRelation<E, R>>
                          {
                          public List<R> getParents();
                          public List<R> getChildren();
                          public List<E> listDescendants() ;
                          }
                          public interface NodeRelation<E extends Node<E, R>,
                          R extends NodeRelation<E, R>>
                          {
                          public E getParent();
                          public E getChild();
                          }
                          abstract class ANode<E extends ANode<E,R>,
                          R extends ARelation<E,R>>
                          implements Node<E,R> {
                          abstract protected E getThis() ;
                          public List<E> listDescendants()
                          {
                          List<E> result = new ArrayList<>();
                          E root = getThis() ;
                          ...
                          return result;
                          }

                          }

                          abstract class ARelation<E extends ANode<E,R>,
                          R extends ARelation<E,R>>
                          implements NodeRelation<E,R> {
                          }

                          class CNode extends ANode<CNode, CRelation> {
                          public CNode getThis() { return this ; }
                          ...
                          }

                          class CRelation extends ARelation<CNode, CRelation> {
                          ...
                          }


                          Although I might not bother with having both abstract class and interface layers.






                          share|improve this answer














                          With this sort of situation it is often useful to have a getThis method that (by convention) returns this.



                          I would do the following



                          public interface Node<E extends Node<E, R>,
                          R extends NodeRelation<E, R>>
                          {
                          public List<R> getParents();
                          public List<R> getChildren();
                          public List<E> listDescendants() ;
                          }
                          public interface NodeRelation<E extends Node<E, R>,
                          R extends NodeRelation<E, R>>
                          {
                          public E getParent();
                          public E getChild();
                          }
                          abstract class ANode<E extends ANode<E,R>,
                          R extends ARelation<E,R>>
                          implements Node<E,R> {
                          abstract protected E getThis() ;
                          public List<E> listDescendants()
                          {
                          List<E> result = new ArrayList<>();
                          E root = getThis() ;
                          ...
                          return result;
                          }

                          }

                          abstract class ARelation<E extends ANode<E,R>,
                          R extends ARelation<E,R>>
                          implements NodeRelation<E,R> {
                          }

                          class CNode extends ANode<CNode, CRelation> {
                          public CNode getThis() { return this ; }
                          ...
                          }

                          class CRelation extends ARelation<CNode, CRelation> {
                          ...
                          }


                          Although I might not bother with having both abstract class and interface layers.







                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited 6 hours ago

























                          answered 7 hours ago









                          Theodore Norvell

                          7,05841637




                          7,05841637






























                               

                              draft saved


                              draft discarded



















































                               


                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53400009%2fjava-generics-self-reference-is-it-safe%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              404 Error Contact Form 7 ajax form submitting

                              How to know if a Active Directory user can login interactively

                              TypeError: fit_transform() missing 1 required positional argument: 'X'