3

I've got a compile error which has me a bit stumped. It says:

TTTree<K,V> cannot be converted to TTTree<K,V> at the marked line of code below.

public abstract class TTTree<K extends Comparable<K>, V> {

  public abstract TTTree<K, V> put(K k, V v);

  private static <K extends Comparable<K>, V> TTTree<K, V> leaf(K k, V v) { return null; }

  private static class Leaf<K extends Comparable<K>, V> extends TTTree<K, V> {

    private final K k = null;
    private final V v = null;

    @Override
    public TTTree<K, V> put(K ik, V iv) {
        TTTree<K, V> newLeaf = leaf(ik, iv);
        return node(newLeaf, k, this); // <---- !!!! ERROR !!!!
    }
  };

  private TTTree<K, V> node(TTTree<K, V> l, K k, TTTree<K, V> r) {
    return null;
  }
}

Seems like this should work, so why doesn't the compiler like it? And what could I do to fix the error (I guess I could convert everything to raw types, but that's a bit too drastic).

PS: Sample code cut down to 'bare minimum' while still producing the error.

PS2: Compiling using Java 8 compiler.

Kris
  • 3,898
  • 1
  • 23
  • 32
  • I actually just figured out a fix. I make the 'node' method static (and change the signature to bind fresh K and V in it... etc) then it compiles with no errors. Still I don't quite understand why that's even necessary. So if someone could answer that... I'd be grateful. – Kris May 21 '16 at 18:48
  • 2
    This is the "private method called from within an anonymous class" issue. I walk through the specification for why it breaks [here](http://stackoverflow.com/a/28971617/2891664), although your compiler error is a little different. The Java compiler tries to call `node` on the enclosing instance even though `Leaf` is a static class and there is no enclosing instance. You can solve it by making `node` `protected` or using `super.node` to call it. – Radiodef May 21 '16 at 19:19
  • @Radiodef: I see why the OP's code would be forbidden, based on your linked answer -- and indeed, adding `super.` fixes it -- but the error-message here seems very strange. Why is the compiler complaining about the type parameters? – ruakh May 21 '16 at 19:28
  • @Radiodef "Marked as an *exact* duplicate". I think you hit the nail on the head with your explanation. So KUDOs, and thanks, great analysis. So the answer to my questions is indeed in there somewhere. But still, looking at the two questions side-by-side... its a bit hard to see how they are 'exact duplicates' :-) – Kris May 21 '16 at 19:43
  • @ruakh If there *was* an enclosing instance (as if `Leaf` was not a static class), the incompatible types error would make sense because `Leaf`'s `` is unrelated to `TTree`'s ``. I don't think there is clear specification for why we get this error instead of "non-static method in a static context". To my knowledge, the JLS does not clearly state when the "no n'th lexically enclosing instance of this" error happens (just that it does), but it's implied that it happens after overload resolution which is when the incompatible types error should occur. – Radiodef May 21 '16 at 19:55
  • @Radiodef: OK, I see what you're saying. So the compiler is like, "oh, hey, cool, you're referring to the `node` method of the lexically enclosing instance -- but wait, `error:` the type arguments aren't necessarily the same!", and it never gets around to noticing that there isn't actually a lexically enclosing instance? That's still weird -- I'd have thought it would "notice" that `Leaf` is a static nested class *before* it would dive into overload resolution, but OK. Thanks! – ruakh May 21 '16 at 20:01
  • 1
    *"its a bit hard to see how they are 'exact duplicates'"* Well, my apologies. I'm happen to clarify anything. I could post an answer, I'm just not sure there is actually much I can add. The private method/enclosing instance is the crux of the issue and just complicated slightly here because of the type variables. Note that as I said in my answer to the other question, the fact that OP used `enum` there is completely irrelevant. – Radiodef May 21 '16 at 20:03
  • @ruakh That's right. Although the JLS doesn't explicitly say that one error occurs before the other, the section ordering of 15.12 implies it. It's quirky either way but it's not really uncommon for a compiler to show a "wrong" error message like that. – Radiodef May 21 '16 at 20:10

1 Answers1

0
public abstract class TTTree<K extends Comparable<K>, V> {

        public abstract TTTree<K, V> put(K k, V v);

        private static <K extends Comparable<K>, V> TTTree<K, V> leaf(K k, V v) { return null; }

        private static class Leaf<K extends Comparable<K>, V> extends TTTree<K, V> {

            private final K k = null;
            private final V v = null;

            @Override
            public TTTree<K, V> put(K ik, V iv) {
                TTTree<K, V> newLeaf = leaf(ik, iv);
                return node(newLeaf, k, this); // <---- !!!! ERROR !!!!
            }
        };

        private static <K extends Comparable<K>,V> TTTree<K, V> node(TTTree<K, V> l, K k, TTTree<K, V> r) {
            return null;
        }
    }

Leaf is a nested static class. It knows nothing about TTTree and its generic types. Also, Leaf::put method invokes TTTree::node method that's not static but belongs to some TTTree instance. At the time when you call Leaf::put there is no link to the TTTree instance you're invoking the node method on. So that the method TTTree::node should be static. The last thing: since TTTree::node is static, you should declare the same type bounds as you use in the Leaf class

Alex K.
  • 714
  • 4
  • 14
  • Could you add some explanation of what the problem is, what you've changed, and why your change fixes it? (The problem with just posting a corrected version of the whole class is that it's really hard to see what the correction is, let alone what the reason is.) – ruakh May 21 '16 at 19:23
  • I got to the same fix with a bit of trial and error, but it would be nice to explain why this change is even needed. I won't downvote the asnwer though... as... at least it works. – Kris May 21 '16 at 19:27
  • Leaf is a nested static class. It knows nothing about TTTree and its generic types. Also, Leaf::put method invokes TTTree::node method that's not static but belongs to some TTTree instance. At the time when you call Leaf::put there is no link to the TTTree instance you're invoking the node method on. So that the method TTTree::node should be static. The last thing: since TTTree::node is static, you should declare the same type bounds as you use in the Leaf class – Alex K. May 21 '16 at 19:28
  • @AlexK.: When someone asks you to elaborate on an answer (or a question, for that matter), they mean that you should *edit* it. (It's fine to post a reply as well, of course, but it's no substitute.) – ruakh May 21 '16 at 19:38
  • @AlexK. "At the time when you call Leaf::put there is no link to the TTTree instance you're invoking the node method on" I don't think that's right. There is an instance because `Leaf` is a subclass of `TTTree`. However (from @Radiodef answer) we can't call the method inherited from parent because its private. In other words... the explanation you provided is 'almost right'. But not quite since it contains a factual error. – Kris May 21 '16 at 21:32
  • @AlexK. I'd accept your answer as its pretty good summary, but it still contains something that I think is incorrect (see my previous comment from May 21st). You want to fix it? – Kris Jun 05 '16 at 00:23
  • @Kris Thanks, but I wouldn't. If you want to post the correct answer, feel free to do it and mark the correct. – Alex K. Jun 07 '16 at 13:56
  • @AlexK. I tried to fixup your anwer instead. If the edit gets approved then I'll mark this version of the answer as accepted. – Kris Jun 20 '16 at 18:24