An excellent Java puzzler

Discussions

News: An excellent Java puzzler

  1. An excellent Java puzzler (43 messages)

    Jevgeni Kabanov, one of the authors of JavaRebel, posted what he termed "the ultimate Java puzzler," which exposes some intricacies of the Java method chaining system. The puzzler was documented, but not exactly obvious unless you actually ran into it and looked it up. The issue is, as stated, method chaining. Given a method "m()" in C1, extended by C2, and finally, extended by C3, the exact implementation of m() called from an instance declared as C1 depends on whether a class can access a superclass' version of a method is the subclass a part of the method chain. Read that four times fast to yourself if it doesn't sound interesting. As stated, the actual implementation details are included in the JVM specification, but it's one of those corner cases that one's not likely to run into and thus you're not likely to anticipate until it's truly turned into a thorn in your side.

    Threaded Messages (43)

  2. Come on...[ Go to top ]

    Come on, this is basic one-o-one stuff. Then probably the following code snippet is really going to rock your world! (Integer.MAX_VALUE + 1) == Integer.MIN_VALUE
  3. Re: Come on...[ Go to top ]

    Is it really? Personally, given the code he illustrated, I would have gotten it wrong.package m1; public class M1 { void doIt() {System.out.println("M1");} } package m1; public class M2 extends M1 { void doIt() {System.out.println("M2");} } package m2; public class M3 extends M2 { void doIt() {System.out.println("M3");} } package m1; public static void main(String[] args) { M1 m=new M3(); m.doIt(); }This will show "M2" -- not M3, even though M3's doIt() should be invoked, based on what a vtable would indicate. It's not a common thing; I wouldn't have put main() in the m1 package, and if it's not local to M1 you won't see the behavior (it won't compile). But it's still an interesting corner case, and I think it's not obvious unless you've run into it.
  4. Re: Come on...[ Go to top ]

    It's not a common thing; I wouldn't have put main() in the m1 package, and if it's not local to M1 you won't see the behavior (it won't compile).
    Actually if you use package visibility it's not uncommon. Just likelier you'd put a public method "publicDotItAndMore()" to M1 or M2 and be surprised that M3 doesn't override "doIt()".
  5. Re: Come on...[ Go to top ]

    Well, TBH, I hardly ever use package visibility - one of the side-effects of chasing this for myself was that I realised how automatic it was for me to specify the actual access level for methods when I code. I actually had to force myself to use the default access level - and it looked funky. It was also a chore for me to put the main() method in the same package as M1 and M2. :)
  6. It really is[ Go to top ]

    Is it really?</blockquote It is, really... It has nothing to do with the term 'method chaining'. Method chaining is what we often see with, for instance, with a StringBuilder: sb.append("text").append("another text").append("etc"); Key terms for this behaviour are: - overiding - package protected - shadowing
  7. It really is[ Go to top ]

    Can't seem to edit posts? Anywho, I meant: package private instead of package protected which is the term for the default method access modifier...
  8. Re: It really is[ Go to top ]

    Is it really?
    It is, really...
    It is amazing how much disagreement there is on this topic. There was quite a discussion next to the blog post, on Twitter and privately with me. People seem to be divided some finding it trivial and some finding it complicated. I obviously side with the latter ones, but it's quite a subjective issue.
  9. Whis is this testing for?[ Go to top ]

    Is this really because the question is hard, or it is triky? Because this is not a quiz on whether the classes written this way actually compile, I assumed the code compiles, and did not even look at the package statement in M2. I just hope all these types of quiz or puzzles do not rely on formatting or ambiguous/confusing snippets to make them harder or more interesting.
    Is it really?

    Personally, given the code he illustrated, I would have gotten it wrong.package m1;
    public class M1 {
    void doIt() {System.out.println("M1");}
    }

    package m1;
    public class M2 extends M1 {
    void doIt() {System.out.println("M2");}
    }

    package m2;
    public class M3 extends M2 {
    void doIt() {System.out.println("M3");}
    }

    package m1;
    public static void main(String[] args) {
    M1 m=new M3();
    m.doIt();
    }This will show "M2" -- not M3, even though M3's doIt() should be invoked, based on what a vtable would indicate.

    It's not a common thing; I wouldn't have put main() in the m1 package, and if it's not local to M1 you won't see the behavior (it won't compile). But it's still an interesting corner case, and I think it's not obvious unless you've run into it.
  10. Re: Come on...[ Go to top ]

    This will show "M2" -- not M3, even though M3's doIt() should be invoked, based on what a vtable would indicate.
    You assertion that M3's doIt() method should be invoked is just plain incorrect. As far as the JVM is concerned, there is no relationship between the method doIt on M1 and the method doIt on M3. The method M1.doIt is not visible to M3 and therefore M3 cannot override it. The names are irrelevant after compilation. Change M3's doIt() to public and make your main method this: public static void main(String[] args) { M1 m1=new M3(); m1.doIt(); M3 m2=(M3) m1; m2.doIt(); } Frankly this is the kind of thing that I got hung up on when I had about 2 or 3 years of experience programming with Java but it's by no means an expert level puzzle. Java didn't really have many expert level puzzles until 1.5. Having said that, it is easy to get caught by this even once you understand why it happens. This is why the @Overrides annotation was added and is quite useful for saving you from pulling your hair out.
  11. Perception is the issue here[ Go to top ]

    It's not a common thing; I wouldn't have put main() in the m1 package, and if it's not local to M1 you won't see the behavior (it won't compile). But it's still an interesting corner case, and I think it's not obvious unless you've run into it.
    It is common to have a main() in a package to do unit testing. And I could imagine writing the code to actually invoke this type of issue through re-using a set of tests already written. What makes this a hard problem to deal with (at least for me) is not that the underlying mechanisms are too complex to understand. Rather it is a perception. I never use the default - normally only public and private. Protected is rare. So when I read throug the code, I basically ignore the visibility and assume that the code is written in the same style as I would use.
  12. Re: Come on...[ Go to top ]

    Why is this particular one the ultimate? Two reasons: * It melted my brain when I hit it.
    It melted YOUR brain? That surely makes this one "the ultimate".
  13. Re: Come on...[ Go to top ]

    > Why is this particular one the ultimate? Two reasons:
    > * It melted my brain when I hit it.

    It melted YOUR brain? That surely makes this one "the ultimate".
    It does for me :) And isn't that the whole difference of blogs from traditional journalism -- I write my own opinion.
  14. Re: An excellent Java puzzler[ Go to top ]

    As for the "ultimate" thing http://www.youtube.com/watch?v=8x1z8IK-L0U at 1:08 will give you a clue as to how I choose the titles for my blog ;)
  15. Re: An excellent Java puzzler[ Go to top ]

    Having read the original post I would have to say ... why on earth is this being posted on this website? I could imagine it being on http://www.java4n00bs.com/, but not here!
  16. Re: An excellent Java puzzler[ Go to top ]

    The issue is, as stated, method chaining. Given a method "m()" in C1, extended by C2, and finally, extended by C3, the exact implementation of m() called from an instance declared as C1 depends on whether a class can access a superclass' version of a method is the subclass a part of the method chain.
    As far as I know, the 'method chaining' you describe is not real; not in Java. Or my understanding of your explanation doesn't match with Java's method resolution. IYour explanation of this seems to me kind of like using epicycles to explain the apparent motion of Mars. When you call m() on a C1 reference, the JVM starts at the C3 class looking for an overridden version of C1.doIt(). C3 contains no such method. It contains a method called doIt() defined on C3 but as far as the JVM is concerned, there is no relationship between these methods. The 'name' it sees for C1.m() and C3.m() are completely different. So it finds no such method on C3 and looks in the next super class for an overridden version of m(). On C2, it finds one and that's what is called. (This process may not actually happen on every call as it can be optimized out but these are the semantics of the call). Here's a simpler and easier to understand (IMO) example of this and it's also something that a little more common. It requires no packages. You can just plop the following into Example.java and go. public class Example { public static void main(String[] args) { Example example = new Extenze(); example.test(); } public final void test() { doTest(); } public void doTest() { System.out.println("example here"); } } class Extenze extends Example { public void doTest() { System.out.println("extenze here"); } } When you run Example, you should see 'extenze here'. Now, change Example.doTest() to be private and recompile. Run again and you will see 'example here'.
  17. Re: An excellent Java puzzler[ Go to top ]

    Here's a simpler and easier to understand (IMO) example of this and it's also something that a little more common.
    Now that I think about it, this isn't really the same situation because the private method cannot be overridden (even by an inner class with access to it.) But it's a similar kind of situation.
  18. Re: An excellent Java puzzler[ Go to top ]

    The post got updated now, some even cooler issues came out.
  19. Re: An excellent Java puzzler[ Go to top ]

    The post got updated now, some even cooler issues came out.
    The simplest way to think about it is: comment out m() on C3. Now, does the way C4 works surprise you? The method m() on C3 doesn't exist as far as C4 is concerned just as the method m() on C2 doesn't exist as far as C3 is concerned. Java is not python. The names are only are there for humans.
  20. Re: An excellent Java puzzler[ Go to top ]

    You know, the thing isn't how common this problem is, or whether someone who really knows Java knows about it, or even if it's logical based on how you think about the problem. It is logical; it's even obvious for some on first glance. (It wasn't for me.) The thing is that it's a puzzler that I think is illustrative for a majority; I don't think the majority will necessarily run into it, based on extrapolation of my own experience (it took writing unusual code for me to even see the behavior.) It's interesting. Is it super-important? I doubt it; normal coding practices would hide the behavior. But it's interesting to me, at the very least; it's quite all right if it's not interesting to you.
  21. Re: An excellent Java puzzler[ Go to top ]

    I don't think the majority will necessarily run into it, based on extrapolation of my own experience (it took writing unusual code for me to even see the behavior.)
    I disagree. This problem and ones similar to it are very common questions on Java programming forums. I can't count the number of times I've addressed it. It's also a common enough class of problems that a special annotation @Overrides was created to prevent it from occurring (one which I strongly recommend using even if it's not completely accurate.) Scala actually has a keyword overrides that does a similar type of check.
    It's interesting. Is it super-important? I doubt it; normal coding practices would hide the behavior. But it's interesting to me, at the very least; it's quite all right if it's not interesting to you.
    I never said it wasn't interesting. Why would be posting to this thread if I have no interest in this? What I am saying is that instead of trying to invent complex and inaccurate explanations of the behavior, it's better to use the simpler correct explanation. In other words, the confusion over this is a result of an incorrect understanding of method resolution in Java. Don't try to amend that understanding with unending complications, just adopt the correct understanding. It's very similar to the common (in the past at least) description of parameter passing in Java: objects are pass by reference and primitives are pass by value. This description causes a lot of confusion because it fails to explain the way things work accurately. Some people have tried to add all kinds of extra rules to this description to make it work but in the end, the real explanation is so much simpler.
  22. Re: An excellent Java puzzler[ Go to top ]

    It's also a common enough class of problems that a special annotation @Overrides was created to prevent it from occurring (one which I strongly recommend using even if it's not completely accurate.)
    If you read the updated post you saw that @Override doesn't behave correctly in this case (the example don't compile, but work). Don't you think it's a good indicator that this is counter-intuitive? I don't know of any other language that allows shadowing for virtual methods or specially the weird skipping of classes in hierarchy. It may all be understandable, but it's counter-intuitive.
  23. Re: An excellent Java puzzler[ Go to top ]

    If you read the updated post you saw that @Override doesn't behave correctly in this case (the example don't compile, but work).
    I did read it, hence the "even if it's not completely accurate".
    Don't you think it's a good indicator that this is counter-intuitive?
    I never once suggested it was intuitive. Please don't put words into my mouth. All I have said is that this is well understood by Java experts.
    I don't know of any other language that allows shadowing for virtual methods or specially the weird skipping of classes in hierarchy.
    Someone on your blog suggest similar things can be shown with C++ but I haven't used C++ in over a decade so I can't recall whether that's true. And again, you are describing this in a way that only confuses the issue. There is no 'skipping'. When you think about how the class is compiled in Java you must think about it from the perspective of the class being compiled. In the case of C4, there is one inherited m() method that it sees. It's inherited from the C2 class which in turn inherits it from C1. That C3 has a method called m() doesn't have any bearing on this. It's not the 'same' method, that is, it has no polymorphic relationship with the m() declared on C1.
  24. Re: An excellent Java puzzler[ Go to top ]

    Someone on your blog suggest similar things can be shown with C++ but I haven't used C++ in over a decade so I can't recall whether that's true.
    He was referring to static method shadowing, not virtual ones.
    When you think about how the class is compiled in Java you must think about it from the perspective of the class being compiled. In the case of C4, there is one inherited m() method that it sees. It's inherited from the C2 class which in turn inherits it from C1. That C3 has a method called m() doesn't have any bearing on this. It's not the 'same' method, that is, it has no polymorphic relationship with the m() declared on C1.
    In case of Java it is absolutely pointless to think about compilation as compiled classes are absolutely identical to the source classes. All of the structure and code is preserved. Therefore JVM actually has to deal with the invocation decisions at runtime, and considering the way optimizers work it first builds vtables based on walking the hierarchy with all the "skipping" involved. That's how virtual dispatch is implemented, although JIT will aggressively inline the calls if it can. True, once the vtable is constructed, JVM does not anymore take the not accessible methods into account, but this just means that the decision is made ahead-of-time, not that "skipping" never happens. If e.g. you omit the implementation of m() from C4, then C3.m() will exactly be skipped and C2.m() called directly. But the vtable of C4 will be used for that and JVM will have made the skipping decision beforehand. In the end I'm not even sure we have a ground to argue, I think it's just that we prefer different explanations of things. My goal was not to come up with a different explanation than the (correct) one on JLS, I just wanted to work through border cases and understand exactly what it means. It was never meant to be broadcast around the world, but it turned out quite a learning experience for me and (hopefully) for others.
  25. Re: An excellent Java puzzler[ Go to top ]

    I think this is an instructive example that shows what I am trying to explain: -- p1/C1.java -- package p1; import p2.C3; public class C1 { public static void main(String[] args) { C4 c4 = new C4(); C1 c1 = (C1) c4; C2 c2 = (C2) c4; C3 c3 = (C3) c4; System.out.println("(C1): " + c1.m()); System.out.println("(C2): " + c2.m()); System.out.println("(C3): " + c3.m()); System.out.println("(C4): " + c4.m()); } int m() { return 1; } } -- p1/C2.java -- package p1; public class C2 extends C1 { /* no implementation */ } -- p2/C3.java -- package p2; import p1.C2; public class C3 extends C2 { public int m() {return 3;} } -- p1/C4.java -- package p1; import p2.C3; public class C4 extends C3 { /* no implementation */ } When I compile this and run it on a 1.4 JVM I get the following output: (C1): 1 (C2): 1 (C3): 3 (C4): 3 What I don't understand about your explanation is how I would get that output if all the JVM were doing was skipping methods that are not accessible. In this case, all versions of m() are accessible from C4 yet the first two calls print 1. If all it is doing is going through the superclasses and finding the first accessible version, why don't all calls resolve to C3's version? Maybe I am misunderstanding your explanation. It seems to me the simplest explanation is that C3.m() is not the same method (in the polymorphic sense) as C1.m(). Now, for the fun part. Change C3 to: // p2/C3.java package p2; import p1.C2; public class C3 extends C2 { /* no implementation */ } Compile only p2/*.java without compiling anything in p1. Run C1 again. The output is now: (C1): 1 (C2): 1 (C3): 1 (C4): 1 I actually find that slightly surprising but now try the recompiling the classes in p1. You should get these compilation errors: p1/C1.java:15: m() is not public in p1.C1; cannot be accessed from outside package System.out.println("(C3): " + c3.m()); ^ p1/C1.java:16: m() is not public in p1.C1; cannot be accessed from outside package System.out.println("(C4): " + c4.m()); This probably explains why you see this as so surprising and I do not. The VM spec and the JLS are slightly at odds here (the JVM being more lenient.) The compiler says that code isn't valid but the JVM links the calls to C1. I'm much more familiar with the java language than the JVM and give your area of expertise you are probably most familiar with the JVM.
  26. Re: An excellent Java puzzler[ Go to top ]

    Let's start with the less fun part:
    What I don't understand about your explanation is how I would get that output if all the JVM were doing was skipping methods that are not accessible. In this case, all versions of m() are accessible from C4 yet the first two calls print 1.
    JVM spec reads that the target method must be accessible from the selected method class. In the first case the target method is C1.m(). We move from bottom-up:
    1. C4 doesn't have a method m().
    2. C3 has a method m(), but C1.m() is not accessible from it.
    3. C2 doesn't have a method m().
    4. C1 has a method m() and C1.m() is trivially accessible from it.
    That's how I think of it and therefore my explanation. But I'm still kinda confused how to fit into it that methods can also be found above the target, e.g. when the target is C2.m() then last step is still selecting C1.m() and although C2.m() is accessible from it, if the methods would be protected and classes in different packages that would not be the case.
  27. Re: An excellent Java puzzler[ Go to top ]

    Let's start with the less fun part:
    What I don't understand about your explanation is how I would get that output if all the JVM were doing was skipping methods that are not accessible. In this case, all versions of m() are accessible from C4 yet the first two calls print 1.

    JVM spec reads that the target method must be accessible from the selected method class. In the first case the target method is C1.m(). We move from bottom-up:

    1. C4 doesn't have a method m().

    2. C3 has a method m(), but C1.m() is not accessible from it.

    3. C2 doesn't have a method m().

    4. C1 has a method m() and C1.m() is trivially accessible from it.


    That's how I think of it and therefore my explanation. But I'm still kinda confused how to fit into it that methods can also be found above the target, e.g. when the target is C2.m() then last step is still selecting C1.m() and although C2.m() is accessible from it, if the methods would be protected and classes in different packages that would not be the case.
    I don't see where the JVM spec says that. Requoting the section from the spec that quoted in your blog:
    Let C be the class of objectref. The actual method to be invoked is selected by the following lookup procedure: * If C contains a declaration for an instance method with the same name and descriptor as the resolved method, and the resolved method is accessible from C, then this is the method to be invoked, and the lookup procedure terminates.
    In order for this to be an accurate description of how Java works, C in the above has to be the runtime type of the object, not the type of the reference. Assuming that for the moment and your interpretation: 1. Does C4 have a method m()? no -> recurse to C3 2. Does C3 have a method m()? yes 2a. Is C3.m() accessible from C4? yes -> C3.m() is the method to call. But that's not what happens. If you follow my interpretation that C3.m() has a different name and/or descriptor from C1.m() then this process matches the actual behavior.
  28. Re: An excellent Java puzzler[ Go to top ]

    In order for this to be an accurate description of how Java works, C in the above has to be the runtime type of the object, not the type of the reference.
    This confused me too at first. The important part is what is "resolved method". And that is the method that was invoked by the caller, i.e. C1.m(). And it is important if that one is accessible. As someone cleared on the blog even the JVM spec is not entirely correct in case of transitive overriding and JVMs are actually more permissive than spec anyway.
  29. Re: An excellent Java puzzler[ Go to top ]

    In order for this to be an accurate description of how Java works, C in the above has to be the runtime type of the object, not the type of the reference.

    This confused me too at first. The important part is what is "resolved method". And that is the method that was invoked by the caller, i.e. C1.m(). And it is important if that one is accessible. As someone cleared on the blog even the JVM spec is not entirely correct in case of transitive overriding and JVMs are actually more permissive than spec anyway.
    But the method resolution section 5.4.3.3 Method Resolution doesn't mention accessibility until after the recursive search is completed. Again, I have to assume C is the runtime type here and not the reference type otherwise the following would always fail with an IncompatibleClassChangeError (per step 1): Map m = new HashMap(); m.put("test", "test"); So again, if your interpretation (as I understand it) were correct, c1.m() would resolve to C3.m(). The only way the spec can match the demonstrated behavior of this example is if the method C3.m() is not considered to have the same name and/or descriptor as C1.m().
  30. Re: An excellent Java puzzler[ Go to top ]

    So again, if your interpretation (as I understand it) were correct, c1.m() would resolve to C3.m(). The only way the spec can match the demonstrated behavior of this example is if the method C3.m() is not considered to have the same name and/or descriptor as C1.m().
    OK, let's really do things step by step. First the spec:
    If C contains a declaration for an instance method with the same name and descriptor as the resolved method, and the resolved method is accessible from C, then this is the method to be invoked, and the lookup procedure terminates.
    Let's examine the C1 c = new C4(); c.m(); invocation. Here C=C4 (the runtime class, as you said), resolved method = C1.m() (Class name is a part of the resolved method name). Now we move up:
    1. From C4 resolved method C1.m() is accessible (same package), so if C4 has m() we select method C4.m() for invocation and finish.
    2. From C3 resolved method C1.m() is not accessible (different packages).
    3. From C2 resolved method C1.m() is accessible (same package), we select method C2.m() for invocation and finish.
    I think what you don't understand is that for JVM class (or rather owner type as it can be interface) is always a part of the method name.
  31. Re: An excellent Java puzzler[ Go to top ]

    I think what you don't understand is that for JVM class (or rather owner type as it can be interface) is always a part of the method name.
    I've been arguing that C3.m doesn't have the same name as C1.m. You've effectively adopted my argument. How is it that I am misunderstanding?
  32. Re: An excellent Java puzzler[ Go to top ]

    I think what you don't understand is that for JVM class (or rather owner type as it can be interface) is always a part of the method name.
    I've been arguing that C3.m doesn't have the same name as C1.m. You've effectively adopted my argument. How is it that I am misunderstanding?
    But that's not what defines it. C1.m, C2.m, C3.m and C4.m all have different names, but same method names and signatures. Both are important.
  33. Re: An excellent Java puzzler[ Go to top ]

    I think what you don't understand is that for JVM class (or rather owner type as it can be interface) is always a part of the method name.
    I've been arguing that C3.m doesn't have the same name as C1.m. You've effectively adopted my argument. How is it that I am misunderstanding?

    But that's not what defines it. C1.m, C2.m, C3.m and C4.m all have different names, but same method names and signatures. Both are important.
    I never said they weren't.
  34. Re: An excellent Java puzzler[ Go to top ]

    I think what you don't understand is that for JVM class (or rather owner type as it can be interface) is always a part of the method name.
    I've been arguing that C3.m doesn't have the same name as C1.m. You've effectively adopted my argument. How is it that I am misunderstanding?

    But that's not what defines it. C1.m, C2.m, C3.m and C4.m all have different names, but same method names and signatures. Both are important.


    I never said they weren't.</blockquote Never mind then :)
  35. Re: An excellent Java puzzler[ Go to top ]

    This probably explains why you see this as so surprising and I do not. The VM spec and the JLS are slightly at odds here (the JVM being more lenient.) The compiler says that code isn't valid but the JVM links the calls to C1. I'm much more familiar with the java language than the JVM and give your area of expertise you are probably most familiar with the JVM.
    Well, this will keep me awake at nights :( I'd have expected an IllegalAccessError for C3.m(), but apparently not. I'm not even sure if this is legitimate or different JVMs will behave differently here. Have to play some more.
  36. Re: An excellent Java puzzler[ Go to top ]

    BTW you can try giving this test to some friends/colleagues and see how they fare: http://www.classmarker.com/embedded_quizzes/?quiz=5ced4e42711fa927925c23e87b51be94
  37. Re: An excellent Java puzzler[ Go to top ]

    I don't know of any other language that allows shadowing for virtual methods or specially the weird skipping of classes in hierarchy.


    Someone on your blog suggest similar things can be shown with C++ but I haven't used C++ in over a decade so I can't recall whether that's true.

    And again, you are describing this in a way that only confuses the issue. There is no 'skipping'. When you think about how the class is compiled in Java you must think about it from the perspective of the class being compiled. In the case of C4, there is one inherited m() method that it sees. It's inherited from the C2 class which in turn inherits it from C1. That C3 has a method called m() doesn't have any bearing on this. It's not the 'same' method, that is, it has no polymorphic relationship with the m() declared on C1.
    C4 is definitely weird. Note that C4.m does not know about super.m --because super is p2.C3-- but still c.m resolves in the C4.m implementation. Why is m in C4 the same method as m in C1 and C2? Because they are in the same package and have inheritance relationship? It seems that C4 inheriting from p2.C3 has no bearing here.
  38. Re: An excellent Java puzzler[ Go to top ]

    C4 is definitely weird.

    Note that C4.m does not know about super.m
    I could be misunderstanding you but in my example, C3.m() is public so C4 does 'know' about it. It's C3 that doesn't know about C2.m (really C1.m in my example).
  39. Re: An excellent Java puzzler[ Go to top ]

    If you think about difference between overriding and hiding methods, this makes sense, see http://faq.javaranch.com/view?OverridingVsHiding. Java supports overriding and hiding, package and protected methods. If you understand those concepts, these examples are actually intuitive. However, @Override implementation is obviously misleading.
  40. Java Rebel[ Go to top ]

    What implications, if any does this behavior have on JavaRebel? I'm guessing that in my Example class above, if I recompile Example and reload it without recompiling Extenze and reloading it too, the behavior will not reflect that Extends.doTest() not overrides Example.doTest().
  41. Re: Java Rebel[ Go to top ]

    What implications, if any does this behavior have on JavaRebel? I'm guessing that in my Example class above, if I recompile Example and reload it without recompiling Extenze and reloading it too, the behavior will not reflect that Extends.doTest() not overrides Example.doTest().
    No this will work fine as will most of the other stuff. But when you start considering that visibilities on methods can change from public/protected to package private and back it gets quite hairy, exactly due to this weird override behaviour. That was the context I got the example from, thought there are more complicated ones waiting for the next post. And by the way you are wrong to think that from JVM perspective names don't exist. Unlike most native code languages JVM uses the names and signatures all of the time, mainly due to late binding. In fact since more classes can be loaded at any moment it has to add vtables all the time. What you are right about is that once they are assembled the classes in between are skipped.
  42. Re: Java Rebel[ Go to top ]

    And by the way you are wrong to think that from JVM perspective names don't exist. Unlike most native code languages JVM uses the names and signatures all of the time, mainly due to late binding. In fact since more classes can be loaded at any moment it has to add vtables all the time. What you are right about is that once they are assembled the classes in between are skipped.
    I don't mean to suggest that the names are not in the JVM at all. I made my point a little to general. What I mean is in the JVM, the method we call m() is not accessed based on it's textual name (ignoring reflection). The method m() declared on C1 and the method m() on C3 have no relationship as far as the JVM is concerned. The VM doesn't 'skip' C1.m() method. That implies that the VM is keeping some sort of complex rulebook about when to call C3.m() or not. It's really not like that. The call to C1.m() through a C3 has nothing to do with C3.m().
  43. Re: Java Rebel[ Go to top ]

    What implications, if any does this behavior have on JavaRebel? I'm guessing that in my Example class above, if I recompile Example and reload it without recompiling Extenze and reloading it too, the behavior will not reflect that Extends.doTest() not overrides Example.doTest().

    No this will work fine as will most of the other stuff.
    Fine in what way? Will after reloading Example without reloading Extenze, does doTest() print "extenze here" or "example here"?
  44. Re: Java Rebel[ Go to top ]

    What implications, if any does this behavior have on JavaRebel?
    No this will work fine as will most of the other stuff.
    Fine in what way? Will after reloading Example without reloading Extenze, does doTest() print "extenze here" or "example here"?
    Fine as in correct as per Java spec. Looking at your example "example here". But there are some cases with default visibility where supporting visibility changes would be too expensive from performance point of view. You can check our test suite (http://zt-oss.googlecode.com/svn/JavaRebelAutoTest/trunk/incSources/unsupported/), though not everything there reflects conceptual problems, some things are just temporary.