• Post Reply Bookmark Topic Watch Topic
  • New Topic

Referring to an overridden method from a supertype reference?  RSS feed

 
nick woodward
Ranch Hand
Posts: 382
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Got a quick question about overriding methods if anyone can help please!


If a method is overridden but you use a polymorphic (supertype) reference to refer to the subtype object with the overriding method, the compiler assumes you're calling the supertype version of the method.


is this true? maybe i'm misunderstanding it, but i thought the JVM looks at the object at run time and checks the object type. the context of the quote is about checked exceptions, but it seems like the statement should stand regardless of context. but this doesn't back up my experience. for example:



will invoke the subclass method. like i said, maybe i'm missing something. any help would be appreciated!

and, apologies to Campbell Ritchie if you're reading. I've been ill the last week, so haven't had the pleasure of tackling those enums properly yet (although I did have a quick go treating it as an inner class as suggested, and it seemed a bit easier to manage). don't want you to think you put effort into that for nothing!

Thanks!

Nick
 
Henry Wong
author
Sheriff
Posts: 23295
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
nick woodward wrote:
If a method is overridden but you use a polymorphic (supertype) reference to refer to the subtype object with the overriding method, the compiler assumes you're calling the supertype version of the method.

is this true? maybe i'm misunderstanding it, but i thought the JVM looks at the object at run time and checks the object type. the context of the quote is about checked exceptions, but it seems like the statement should stand regardless of context. but this doesn't back up my experience...


I disagree. If the context is in regards to checked exceptions, then you may be taking the quote out of context.

The subclass' overriding method is allowed to be declared to throw less checked exceptions (although not more checked exceptions). If the subclass throws less checked exceptions, and you are using a superclass type, the compiler can't tell that it is throwing less checked exceptions, and will assume that all the checked exceptions (of the superclass) needs to be taken care of.

Or in other words, the quote is not saying which version of the method will be called (obviously, it is the overriding method of the subclass), but which version's exception declaration is used for compilation.

Henry
 
Campbell Ritchie
Marshal
Posts: 56584
172
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Agree with Henry. The compiler does not “bind” (non‑private) instance methods to a particular implementation. The implementation of the method is written in bytecode in the .class file, then copied to the Class<T> object, then the Class<T> object is found at runtime and the method sought in that. As you said yourself, that is how polymorphism goes.At line 3 the compiler does not know whether the factory method will return a Foo or one of its subtypes. So it cannot tell whether in the try whether line 7 might throw an Exception or an IOException; at line 10 it has to play safe and “think”
He said it might throw an Exception and I can't guarantee to catch that with XYZException or IOException.
So the compiler must play safe and insist that Exception be caught (or re‑declared) instead of XYZException.
 
Henry Wong
author
Sheriff
Posts: 23295
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

A modification of the OP's example that shows the issue...

 
Campbell Ritchie
Marshal
Posts: 56584
172
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
We had the same problem a few months back about interfaces which declare an Exception. Callable#call declares that it might throw Exception, so you can legitimately create classes which implement Callable and don't declare any Exceptions. But woe betide you if you create an anonymous class:-
… without wrapping it in a try-catch.
I can't remember what would happen if you create that method as a λ instead.
 
nick woodward
Ranch Hand
Posts: 382
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here is the full text. I guess I have taken it out of context then, but i'm not sure it's a particularly good (or maybe beginner proof ) way of explaining the problem.

I don't understand how or why the compiler assumes you're calling the superclass version of the method - because that isn't usually the case is it? Does it only become the case when exceptions are involved? I understand that the subclass cannot throw exceptions that are broader than the superclass, so the compiler deferring to the superclass makes sense - but i can't quite piece all of this together. Any chance of a 'fisher-price' style explanation please? I think I need it (Campbell - I'll always take your responses as a compliment. You overestimate my level of understanding!! ;) )


If a method is overridden but you use a polymorphic (supertype) reference to refer to the subtype object with the overriding method, the compiler assumes you're calling the supertype version of the method. If the supertype version declares a checked exception, but the overriding subtype method does not, the compiler still thinks you are calling a method that declares an exception (more in Chapter 6).

Let's take a look at an example:




This code will not compile because of the Exception declared on the Animal eat() method. This happens even though, at runtime, the eat() method used would be the Dog version, which does not declare the exception.


thanks!
 
Henry Wong
author
Sheriff
Posts: 23295
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
nick woodward wrote:
I don't understand how or why the compiler assumes you're calling the superclass version of the method - because that isn't usually the case is it?


How about you telling us? Let's say that you are the compiler... and well, you operate only at compile time. The actual type of the object is only determinable at runtime. How will you, as the compiler, determine whether this is the case or not? How will you determine the type of the object (if you are not allow to assume based on the reference)?

Henry
 
nick woodward
Ranch Hand
Posts: 382
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ah ok. Got it I think. Or at least part of it.

So at compile time the reference type has no knowledge of the overridden method, so assumes the exception has to be dealt with. And at runtime, when the object is checked and the method overridden, this causes a problem?

So is the issue that the compiler doesn't know about the override, or the breadth of the exception (or both). I'd guess the former, but I'm not 100% clear on this. Ie is the problem that the compiler expected an exception,and at run time it isn't provided? Am I right in thinking this isn't a problem with a subclass reference?
 
Campbell Ritchie
Marshal
Posts: 56584
172
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Agree. The compiler cannot tell which subclass version you are going to call.At the end of line 1 the compiler does not know that you are going to assign that to a Cat object, and the only information it has is that it is an Animal. The only “safe” course of action is to assume that everything in animal is an Animal version. Since the rules say that an overriding method cannot declare any “new” [checked] Exceptions, the compiler can do this safely.

If you went for the subclass version which throws IOException (as in my post) and you got a different subtype whose method throws different Exceptions from IOE and XYZE, then your program would crash.
 
nick woodward
Ranch Hand
Posts: 382
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:Agree. The compiler cannot tell which subclass version you are going to call.At the end of line 1 the compiler does not know that you are going to assign that to a Cat object, and the only information it has is that it is an Animal. The only “safe” course of action is to assume that everything in animal is an Animal version. Since the rules say that an overriding method cannot declare any “new” [checked] Exceptions, the compiler can do this safely.


is there any particular reason why the compiler cannot know this when the assignment occurs? why does it have to wait for the JVM? i guess the same answer/question applies to overridden methods too.


If you went for the subclass version which throws IOException (as in my post) and you got a different subtype whose method throws different Exceptions from IOE and XYZE, then your program would crash.


so would it be fair to say that the rules are: an overriding method cannot throw new or broader exceptions, and must mirror the exception of the parent if a superclass reference type is used? are narrower exceptions therefore prohibited when using a superclass reference? (am going to go and try and test this myself )

edit: *and unchecked omissions are fine in the subclass.

i figured out what my confusion with this was:


If a method is overridden but you use a polymorphic (supertype) reference to refer to the subtype object with the overriding method, the compiler assumes you're calling the supertype version of the method.

they don't emphasise that the compiler assumes it. my confusion was that i was thinking (and my test code demonstrated) the JVM in action.

got it.

thank you very much, both of you!

nick
 
nick woodward
Ranch Hand
Posts: 382
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
to partially answer one of my questions above - i'm not sure about a narrower exception type thrown by the subclass when the superclass reference is used. the compiler complains, but only that 'it cannot find' the exception type, so i'm not 100% sure if narrowing is the problem.
 
Campbell Ritchie
Marshal
Posts: 56584
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
nick woodward wrote: . . . is there any particular reason why the compiler cannot know this when the assignment occurs? . . .
You are compiling from left to right. When you get to the Animal declaration, the compiler has not yet looked at the bit about new Cat(), so it cannot “know” whether you are going to assign to a Cat or not.Now, even the human eye, which is much better at seeing such things than the compiler, cannot tell whether eat(); is being called on a Cat instance or a Dog instance.Now you don't know whether to catch PRQ exceptions or XYZ exceptions; the only kind you can catch is plain simple Exception.

That is consistent with the general implementation of polymorphism: you check at compile time which methods are present in Animal and the exact version used depends on whether it is a Cat or a Dog. That would apply even if Animal were an abstract class.
. . . An overriding method cannot throw new or broader exceptions, and must mirror the exception of the parent if a superclass reference type is used? are narrower exceptions therefore prohibited when using a superclass reference? (am going to go and try and test this myself )

edit: *and unchecked omissions are fine in the subclass.

i figured out what my confusion with this was:


If a method is overridden but you use a polymorphic (supertype) reference to refer to the subtype object with the overriding method, the compiler assumes you're calling the supertype version of the method.

they don't emphasise that the compiler assumes it. my confusion was that i was thinking (and my test code demonstrated) the JVM in action.

got it.

thank you very much, both of you!

nick
Answer to follow later.
 
nick woodward
Ranch Hand
Posts: 382
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:
nick woodward wrote: . . . is there any particular reason why the compiler cannot know this when the assignment occurs? . . .
You are compiling from left to right. When you get to the Animal declaration, the compiler has not yet looked at the bit about new Cat(), so it cannot “know” whether you are going to assign to a Cat or not.Now, even the human eye, which is much better at seeing such things than the compiler, cannot tell whether eat(); is being called on a Cat instance or a Dog instance.Now you don't know whether to catch PRQ exceptions or XYZ exceptions; the only kind you can catch is plain simple Exception.

That is consistent with the general implementation of polymorphism: you check at compile time which methods are present in Animal and the exact version used depends on whether it is a Cat or a Dog. That would apply even if Animal were an abstract class.


ah, of course, much appreciated!

 
Campbell Ritchie
Marshal
Posts: 56584
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
nick woodward wrote: . . . an overriding method cannot throw new or broader exceptions, . . .
Spot on.

It would be very bad design to throw Exceptions which the user doesn't expect. Look at the Object#equals method, you will see it never throws Exceptions. (Well, actually it says always return, and you can't have an Exception and return anything, so no exceptions.) Now, what if you forget a null check in a class you have written? In that case you will pass null and get an exception when you are supposed to get false returned. The difference is that the javac tool never notices an unchecked exception.

I think as a rule of thumb, the compiler checks the top line of the method (@Override or return type to throws) and records it. That ensures that all calls to that method have the correct type of argument, correct return type, correct exception handling, etc.
Then the method body between {} is written in the .class files and chosen at runtime.

“Cannot find” from the compiler sounds as if you had forgotten an import statement.

And … you're welcome
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!