According to the documentation, this class is an interface, which Progress.lang.Proerror implements.
If it is an interface, how does the following code work ? (I've got a really bad feeling that I'm being stupid about this)
CATCH e AS Progress.Lang.Error :
MESSAGE "error:" e:getmessage(1) VIEW-AS ALERT-BOX.
DELETE OBJECT e.
END CATCH.
as far as I was aware, an interface cannot define the contents of a method, only that a class must implement such a method.
So, how does e:getmessage(1) actually work ?
as far as I was aware, an interface cannot define the
contents of a method, only that a class must
implement such a method.
That's right. It's abstract.
So, how does e:getmessage(1) actually work ?
e is defined as a variable of the Interface type. This way I can call the methods defined in the Interface - and only the methods defined in the Interface.
That's normal behaviour. The whole concept of Interfaces would be useless if I could not call methods of classes implementing the Interface on a variable of the Interface type.
In a way the Interface is like a SUPER class (without implementation). If you only have a reference of a SUPER class (base.Entity or Progress.Lang.Object or System.Control) you only have access to the members defined in that base class.
You need to CAST to another type in order to get access to more members.
That's right. It's abstract.
ok ...
e is defined as a variable of the Interface type.
This way I can call the methods defined in the
Interface - and only the methods defined in the
Interface.
ok ...
That's normal behaviour. The whole concept of
Interfaces would be useless if I could not call
methods of classes implementing the Interface on a
variable of the Interface type.
but if e is an interface, the method GetMessage does not have any implementation. So how can it actually return some data ?
See, I told you I was feeling stupid ...
but if e is an interface, the method GetMessage does
not have any implementation. So how can it actually
return some data ?
e is an instance variable of an Interface type. The compiler only knows about the methods defined in the Interface. Everything else is hidden.
But at runtime it points to an actual instance of a concrete type. So there must be an implementation for GetMessage and others. That is garanteed!
See, I told you I was feeling stupid ...
OO is much more than substituting Procedure statements with method statements. That shift must make you feel stupid. I felt like learning coding again.
e is an instance variable of an Interface type. The
compiler only knows about the methods defined in the
Interface. Everything else is hidden.
That I understand
But at runtime it points to an actual instance of a
concrete type. So there must be an implementation for
GetMessage and others. That is garanteed!
But where is the implementation implemented ? If I wanted to try to duplicate this, I would first create an interface class foobar.
USING Progress.Lang.*.
INTERFACE foobar:
METHOD PUBLIC LOGICAL foo():
END INTERFACE.
if I were then to say
DEF VAR a as foobar.
message a:foo() view-as alert-box.
what would it do ?
I guess what I am trying to get at is
where is the code that retrieves the message stored at position 1 in the GetMessage(1) method of the progress.lang.error interface ?
where is the code that retrieves the message stored
at position 1 in the GetMessage(1) method of the
progress.lang.error interface ?
Aaah! We are getting to the point. Where is the FOR EACH implemented?
I'd say it's sealed in some safe place in the woods of Bedford or Nashua. The core objects like the error classes/interfaces, Progress.Lang.Object, Progress.Lang.Class, Progress.Data.BindingSource, etc. are part of the C code that make the Progress client.
That's not given to us. They cheat on us a little bit Or protect us from the dirty bits.
Oh, and another thing !
Why can you do
NEW AppError("foo") /* message only */
NEW AppError("foo",1) /* message and severity */
but you can only do
AddMessage("bar",0)
why isn't there an override
AddMessage("foo")
what would it do ?
Output a >runtime
You need to NEW a - between DEF VAR and a:foo() .
a = NEW SomeTypeThatImplementsFoobar () .
NEW AppError("foo") /* message only */
Did you read the docs?
The constructor with the single character parameter does not set an error message. It set's the RETURN-VALUE attribute of the AppError.
That's not given to us. They cheat on us a little bit
Or protect us from the dirty bits.
So, it's exposed to us as an interface, but in the background there is an object created called progress.lang.error that implements the interface progress.lang.error ?
yup, I knew that. I was being a devil's advocate.
Error Messages always need the integer message number.
Did you read the docs?
Several times. My head hurts
The constructor with the single character parameter
does not set an error message. It set's the
RETURN-VALUE attribute of the AppError.
Thanks.
Ok, so if I wanted to add a message with just the text, I would create a new class inheriting apperror, and add the following method.
METHOD PUBLIC VOID AddMessage(p_message AS CHAR):
SUPER:AddMessage(p_message,0).
RETURN.
END METHOD
Not exactly. The actual error object is not a Progress.Lang.Error. It's of one of the types implementing the Progress.LangError.
Progress.Lang.SysError for instance.
Exactly. And you could add a lot more here. You could add translation, persistent logging etc.. Anything that itself does not raise a runtime error.
Not exactly. The actual error object is not a
Progress.Lang.Error. It's of one of the types
implementing the Progress.LangError.
Progress.Lang.SysError for instance.
urk, ok, I surrender (give up)
Exactly. And you could add a lot more here. You could
add translation, persistent logging etc.. Anything
that itself does not raise a runtime error.
Yeah, done that already - it's really handy. I'm doing a workshop soon on error handling and it's a really great example on how to use the new stuff.
thanks for the additional info.
urk, ok, I surrender (give up)
Does that mean the light bulb went on or that you were overwhelmed.
If the latter, step back for a moment from the context of error handling and consider how interfaces get used in the general sense. Suppose I have three classes A, B, and C which all implement interface I. I've got some code which might instantiate any one of these three depending on circumstances. Then, I hand over the object I have instantiated to somewhere else which doesn't know which of the three it is. I can do this because I can define the local variable containing the class as the interface. That let's me get at the common methods that are defined in the interface, but I have to cast it to the right type in order to get at the rest. This is similar to defining the variable as a superclass, using methods in the superclass, and then casting to the subclass when it becomes necessary, only by using an interface the classes don't need to be in the same class hierarchy.
So, in the error handling context, the place the error is thrown is creating an object of a specific type, but we don't necessarily know what type. What we do know, however, is that it will implement the Progress.Lang.Error interface. So, that's what the type is of e.
There is a very standard philosophy in OO of "programming to the interface".
So, in the error handling context, the place the
error is thrown is creating an object of a specific
type, but we don't necessarily know what type. What
we do know, however, is that it will implement the
Progress.Lang.Error interface. So, that's what the
type is of e.
And then there are those occasions, where you'll prefer not to catch the Progress.Lang.Error everywhere, because you might prefer to handle different types of possible (expected in various locations) error types at different levels at your code.
Usually you'll handle the most expected error close to the location the error might occur because you actually might do something useful except to display a message.
The CATCH for Progress.Lang.Error will CATCH any error.When I expect errors from my routines, I usually catch for somethine that inherits from Consultingwerk.Exceptions.Exception (sorry, I could not resist to call it Exception).
And then you'll start to realize how nice it would have been if Progress would have created an error hierachy for runtime errors like in other pure OO languages. So to make efficient use of subclassing errors you need to create your own errors (inheriting AppError) and throw them whenever an expected runtime error (SysError) needs handling.
I think it would be very interesting to start sharing some examples of error handling code. It seems like an area where we could benefit by seeing what other people do.