10 Replies Latest reply on Apr 20, 2010 10:34 AM by Di1bert

    Problems casting to an interface

    Di1bert

      I have the following interface/class hierarchy:

       

      public interface RemoteResponse { ... }
      
      public class RemoteResponseImpl implements RemoteResponse { ... }
      

       

      In a result handler for an RPC call, I need to cast the result to the expected Type:

       

      override public function result(data:Object):void
      {
          var resultEvent:ResultEvent = data as ResultEvent;
          var remoteResponse:RemoteResponse = resultEvent.result as RemoteResponse;
          logger.debug("remoteResponse = {0}", remoteResponse);
      }
      

       

      When I do this the remoteResponse variable ends up being null. The logging output is:

          remoteResponse = null

      And the rest of the function's code fails due to the remoteResponse variable being null.

       

      However, if I cast to the actual concrete implementation:

       

      var remoteResponse:RemoteResponse = resultEvent.result as RemoteResponseImpl;
      

       

      It works. Here the logging ouput is:

          remoteResponse = [object RemoteResponseImpl]

       

      But what is even more bizzare, is this code works:

       

      override public function result(data:Object):void
      {
          var resultEvent:ResultEvent = data as ResultEvent;
          var remoteResponse:RemoteResponse = resultEvent.result as RemoteResponse;
          var unused:RemoteResponse = resultEvent.result as RemoteResponseImpl;
          logger.debug("remoteResponse = {0}", remoteResponse);
      }
      

       

      The only difference in this code from the original code is a secondary cast after the initial cast of the result object to the concrete class. Yet this allows the code to run OK as my remoteResponse variable is no longer null, whoch is relfected in the logging output:

          remoteResponse = [object  RemoteResponseImpl]

      Why is this?  And how can I get it so I can cast to the interface without having to put the details of the implementation class in my code?

        • 1. Re: Problems casting to an interface
          Flex harUI Adobe Employee

          The data has to map to an actual class instance.  It can't just map to an

          interface.  An interface can't store data.  The class that implements the

          interface can.

          • 2. Re: Problems casting to an interface
            Di1bert Level 1

            Thanks for the response. I could use some additional information however...

             

            I'm trying to understand your response, but it doesn't make sense to me. The whole point of an interface is to allow for polymorphic assignment of an object at runtime. We'll like have a few dozen actual class instances that implement the interface. And we won't necessarily know what implementation it is going to be getting at compile time. Hence the use of an Interface.  We simply know it'll be an object that implements the RemoteResponse Interface.

             

            Also, your response doesn't seem to explain why the second code snippet works. Casting to the interface works when there is also a line of code that casts the same object to the class instance, even when that happens after the casting to the interface. That really makes no sense to me. I'd have to believe that something is happening at compile time so that at runtime the code now knows what the instance actually is, whereas without that additional line of code it does not.

            • 3. Re: Problems casting to an interface
              Sebastien V. Level 3

              There's another way of doing casts in Flex:

               

              var remoteResponse:RemoteResponse = RemoteResponse( resultEvent.result );

               

              The difference is that the 'as' synthax will return null if the object can not be casted to the class, whereas the synthax above will throw an Error.

              This synthax should work fine with interfaces.

              • 4. Re: Problems casting to an interface
                Di1bert Level 1

                Thanks for the response Sebastien. I had tried that syntax as well. But it fails as well when I try to cast to the interface. As you mention it throws an error (Error #1034: Type Coercion failed: cannot convert mx.utils::ObjectProxy@23c26e09 to com.foo.RemoteResponse.) rather than returning null.

                 

                So I would still need to cast it to the implementation rather than the interface.

                 

                It's just not making sense to me that you can't cast to an interface. The underlying object implments the interface. So why can I not cast to it? Not being able to do such removes the entire benefit you get from OOP polymorphism & code re-usability/share-ability, and it negates a great many design patterns.

                 

                "Flex harUI" states that "an interface can't store data." Obviously... but the underlying object that implements the interface does store. I simply want my reference variable to represent the interface. For example, in Java, I might cast a result to a List interface, not knowing or caring whether the actual implementation is an ArrayList, LinkedLiast, Vector, FooList, BarList, SomeCrazyList, or any or a few dozen other List implementations. I simply want to interact with the object as a List so I can use the methods and behaviors that a List provides via its contract. And I don't care if the underlying object may also implment the Cloneable, Iterable, Deque, and Queue interfaces as a LinkedList does.

                 

                Am I to gather that Flex/ActionScript does not provide this basic OOP capability?

                • 5. Re: Problems casting to an interface
                  Sebastien V. Level 3

                  The type coercion error shows you that the object you're trying to cast as a "RemoteResponse" is an instance of the mx.utils.ObjectProxy class.

                  Do not ask me why, I have no idea what this ObjectProxy class is. But this explains why the cast fails. The 'as' cast probably takes into consideration this ObjectProxy class so that you can cast it into something else. The other cast, being more strict, tells you that the object you're retrieveing is not of the excepted type.

                   

                  And you can cast to interfaces with the synthax I gave earlier, try on a simple example and you will see for yourself.

                  • 6. Re: Problems casting to an interface
                    pauland Level 4

                    Oops, I have completely the wrong end of the stick..

                    • 7. Re: Problems casting to an interface
                      Gregor.Kiddie Level 2

                      It looks like you've fallen foul of the Flex deserialiser...

                       

                      When you get what should be a strongly typed object back from a service call as an ObjectProxy, it's not been able to convert what's been passed back. Rather than throwing an error, it gives you the object proxy instead.

                       

                      The issue isn't with casting to interfaces, but that Flex isn't recognising the object as a RemoteResponse because the serialiser didn't deserialise it to a concrete class...

                       

                      Look there, the interface thing is a red herring.

                       

                      Gk.

                      • 8. Re: Problems casting to an interface
                        Flex harUI Adobe Employee

                        It does.  You can cast to the interface once you get the deserializer to

                        deserialize to a class that implements the interface.  To get that to

                        happen, you need a class like that both implements the interface and has

                        either metadata or will have registerClassAlias called on its

                        behalf by your code.

                        • 9. Re: Problems casting to an interface
                          Flex harUI Adobe Employee

                          The second code snippet probably worked because linking in the class that

                          implements the interface gave the deserializer the information as to what

                          class to generate when deserializing the data stream.  If no class is

                          registered, it creates ObjectProxy instances.

                           

                          The serialization of objects via AMF is basically a binary format with tags

                          representing class names and fields.  There is no direct mapping of the

                          binary of a server-side object to an Actionscript object.  The deserializer

                          reads the tags and tries to find classes to instantiate in order to shovel

                          the rest of the data in the stream into that class instance's properties.

                           

                          Therefore you will always need to link in at least one class that implements

                          the interface and registers itself with the deserializer.  Just having an

                          interface in the SWF is not sufficient.  However, once deserialized, you can

                          use it via the interface.

                           

                          I imagine in the end, you might register several classes that implement the

                          common interface if you have several classes on the server side that might

                          be in the stream.

                          • 10. Re: Problems casting to an interface
                            Di1bert Level 1

                            Thank you all for the additional information. That gives me something to go on in an effort to try and resolve the issue. As well as having an understanding on what's happening under the surface... an important thing for me personally... I need to know what's happening or I go insane