13 Replies Latest reply: Jun 18, 2011 5:37 PM by 869739 RSS

    funky question

    869739
      I have a situation I hope you can help with. The issue is the type checking on the call to handler.handle(ConcreteTask task), where the receiver is an anonymous subclass of the ConcreteEvent generic, which is the class of the event argument. I put it in bold.

      I was unable to figure out how to post code, and the generic statements were turning into smilies. I put a page up:
      http://reefedjib.org/Service.html

      TVM,
      Rob
        • 1. Re: funky question
          EJP
          private <ConcreteTask extends Task<?>, ConcreteHandler extends ConcreteTask> void handle(ConcreteTask task, ConcreteHandler handler) {
          private <T, ConcreteTask extends Task<T>, ConcreteHandler extends ConcreteTask> void handle(ConcreteTask task, ConcreteHandler handler) {

          But I don't see the point of having both ConcreteTask extends Task and ConcreteHandler extends ConcreteTask. Are there really three levels of inheritance?
          • 2. Re: funky question
            Darryl Burke
            866736 wrote:
            I was unable to figure out how to post code, and the generic statements were turning into smilies.
            Please read the announcements at the top of the forum and the FAQ linked from every page. They are there for a purpose.

            db
            • 3. Re: funky question
              869739
              This changed the situation and seems like a promising direction, but now I have two errors:
               private <T, ConcreteTask extends Task<T>, ConcreteHandler extends ConcreteTask> void handle(ConcreteTask task, ConcreteHandler handler) 
              The second T, in Task<T> complains:

              "Bound mismatch: The type T is not a valid substitute for the bounded parameter <T extends Task<?>> of the type Task<T>"


              And:
              handler.handle(task);
              The handle(task) complains:

              "The method handle(T) in the type Task<T> is not applicable for the arguments (ConcreteTask)"

              But I don't see the point of having both ConcreteTask extends Task and ConcreteHandler extends ConcreteTask. Are there really three levels of inheritance?
              Yes, there are.
              There is the abstract Task,
              there is the concrete subclass TestTask1
              there is the anonymous subclass new TestTask1() { void handle(TestTask1 task); }

              The anonymous subclass handles tasks of class TestTask1, it's superclass.

              Edited by: 866736 on Jun 17, 2011 11:35 PM
              • 4. Re: funky question
                869739
                I also tried to change my Handle to reefedjib and it didn't stick. :(
                • 5. Re: funky question
                  EJP
                  This changed the situation and seems like a promising direction, but now I have two errors:
                  I have one.
                  The second T, in Task<T> complains:

                  "Bound mismatch: The type T is not a valid substitute for the bounded parameter <T extends Task<?>> of the type Task<T>"
                  I don't get this error. What line of code do you get it on?
                  "The method handle(T) in the type Task<T> is not applicable for the arguments (ConcreteTask)"
                  But that error message is correct. ConcreteTask doesn't extend T, it extends Task<T>. Task<T> exports a method handle(T). You're confusing yourself by calling T a 'task'. It isn't. It is the type for which Task is specialized. Alternatively T is a task and the other thing is a TaskHandler<T>. Your type system is presently hopelessly mixed up between these two poles.

                  What I think you're trying to do here is really just this:
                  private <T> void handle(T task, TaskHandler<T> handler)
                  {
                    handler.handle(task); // try/catch elided
                  }
                  and actually I can hardly see the point of having this method at all, except to catch the exception and log it.
                  But I don't see the point of having both ConcreteTask extends Task and ConcreteHandler extends ConcreteTask. Are there really three levels of inheritance?
                  Yes, there are.
                  There is the abstract Task,
                  there is the concrete subclass TestTask1
                  there is the anonymous subclass new TestTask1() { void handle(TestTask1 task); }
                  That's not something you need to express in Generics though. The 'extends' relationship already does that.
                  • 6. Re: funky question
                    869739
                    Thanks for your help. I made some progress by removing the complex Generics in front of the return type of those methods in Service, and now Service, Task, TestTask1 and TestTask2 works. I now have an issue with anonymous subclassing and specifying the Generic there. It is somewhat lengthy, so I have updated my original webpage with the code, per the FAQ. I hope that is ok.

                    http://reefedjib.org/Service.html

                    Note that I must use a separate method handle(Task<?> task, Task handler) to send handle(T task) to the handler, so that the task parameter can be specified with Task, rather than Task<?>.


                    Here is the last bit, the meat:
                    Here’s the weird issue, when I move the Main class to a different package than the one Task is defined in, believe it or not. In this case, the anonymous classes of TestTask1 and TestTask2 report that their implementations of handle(TestTask1 task) are never called. Sure enough it is the case when I run it. Note that I have to have an implementation of handle(T task) in the Task superclass, so we are not forced to define one when we just create a Task to process, instead of installing it as a handler.
                    service.addHandler(new TestTask1() { void handle(TestTask1 task) { System.out.println("task handled: " + task); }; });
                    service.addHandler(new TestTask2() { void handle(TestTask2 task) { System.out.println("task handled: " + task); }; });
                    where handle(TestTask1 task) reports a warning:
                    “The method handle(TestTask1) from the type new TestTask1(){} is never used locally”
                    Here’s the first exception, from the Task.handle(T task) implementation:

                    Jun 18, 2011 8:01:34 AM sandbox.events.Service handle
                    SEVERE: sandbox.events.TestTask2@ca0b6
                    java.lang.IllegalStateException: failed to override the method handle(TestTask2 t) {}
                          at sandbox.events.Task.handle(Task.java:10)
                          at sandbox.events.Service.handle(Service.java:28)
                          at sandbox.events.Service.process(Service.java:21)
                          at sandbox.Main.main(Main.java:37)
                    TVM,
                    Rob

                    Edited by: reefedjib on Jun 18, 2011 5:11 AM
                    • 7. Re: funky question
                      852060
                      Hello Rob,

                      in Java default visibility is "package private". In other words Task<T>.handle(T) is not visible to the inheriting class and thus can not be overridden.

                      With kind regards
                      Ben
                      • 8. Re: funky question
                        EJP
                        You coded:
                        private void handle(Task<?> task, Task handler) {
                        You still haven't cleared up the confusion I mentioned. Task is either a task or a handler. You're using the same type to mean both. Sort it out. As I suggesed:
                        public void handle(T task, Task<T> handler) {}
                        or possibly:
                        public void handle(Task<T> task, TaskHandler<T> handler) {}
                        I can't see any necessary inheritance relationship between a Task and a TaskHandler. It is a parameterization relationship. You're using both inheritance and type-parameterization, and the result is just a meaningless mess.
                        • 9. Re: funky question
                          869739
                          SWEET!!!! Thanks very much for that. These little details get by me sometimes...I think this is the best event system I can come up with. It Genericizes the arg to the handle method and it dispatches on the class of the handler versus the task...handler must handle the task class or a subclass. These seem to be the two pertinent aspects of an event multicast system.

                          What do you think of it?

                          I do love working with Java, but the fact that it is typed does piss (jeez, can't say p i s s? shakes head) me off sometimes. I am a Smalltalker with 14 years experience and am used to occasionally sending disparate types through a particular method as well as using the fact that class side methods (statics) and variables can be overridden and the first class reflection.

                          Here is the equivalent Main code in Smalltalk, assuming the definition of TestTask1 and TestTask2, without the need for a superclass. Note the equivalent to anonymous classes by using blocks, which are full closures.
                          | service |
                          service := Object new.
                          service when: #testTask1 evaluate: [:task | Transcript show: 'handle event: ', task].
                          service when: #testTask2 evaluate: [:task | Transcript show: 'handle event: ', task].
                          service triggerEvent: #testTask1 with: TestTask1 new.
                          service triggerEvent: #testTask2 with: TestTask2 new.
                          It uses the core multicast protocol in Object.

                          Edited by: reefedjib on Jun 18, 2011 5:42 AM
                          • 10. Re: funky question
                            869739
                            >
                            You still haven't cleared up the confusion I mentioned. Task is either a task or a handler. You're using the same type to mean both. Sort it out. As I suggesed:
                            public void handle(T task, Task<T> handler) {}

                            or possibly:
                            public void handle(Task<T> task, TaskHandler<T> handler) {}

                            I can't see any necessary inheritance relationship between a Task and a TaskHandler. It is a parameterization relationship. You're using both inheritance and type-parameterization, and the result is just a meaningless mess.
                            >

                            Not at all. It is a design feature. I am using the Task as both the item to be dispatched (the arg to be handled) as well as the class to be anonymously subclassed as the handler, by overridding the handle(Task t) method. I am using inheritance of the handler to determine the dispatch. TestTask1 subclass + handle() does not get dispatched to handle TestTask2 objects. The Task subclasses specify the parameterization and the inheritance uses:
                            Class<Task<?>> c = (Class<Task<?>>) getClass().getSuperclass();
                            return c.isAssignableFrom(task.getClass());
                            To determine if the handler is the same Task class or a superclass of the task argument. Notice I getClass().getSuperclass() in the handler to get to the real Task subclass (one up from the anonymous class). TestTask1 and TestTask2 are siblings, so they dispatch separately. A real subclass of TestTask1, TestTask1a, WILL dispatch through a TestTask1 handler.

                            So, no need for a separate TestHandler class/subclass, requiring it to be Genericized and a Class object passed in for dispatching.

                            YMMV,
                            Rob

                            Edited by: reefedjib on Jun 18, 2011 5:55 AM
                            • 11. Re: funky question
                              EJP
                              Then you're overcooking it. The type-safety compile errors are telling you that. You don't need to express the grandfathering relationship between abstract tasks and task handlers, for example. That's an implementation detail on the client side, not a required condition of using the API. And your confusion between T and Task<T> remains. The compiler says so.
                              • 12. Re: funky question
                                869739
                                Yeah, you got me thinking and so I separated the Handler from the Task. It's an evolution, right? I made it a non-static inner so it can get the class of the containing Task. Another advantage is it forces the compiler to make you implement a handle method. The code is getting tighter...

                                New code is here: http://reefedjib.org/Service.html


                                Thanks for your help!
                                • 13. Re: funky question
                                  869739
                                  Ok folks, I would like to solicit your opinion on several Task/Handler implementations. Could you take a couple of minutes checking out the creation of a Task handler for registration, and tell me what you like or dislike about each one and which is your choice, please. The various implementations aren't so important, but they all work. I am obviously trying to come up with a coherent event model for use across multiple projects, without all the Swing or GWT cruft.

                                  TVM for your advice and contributions!



                                  I have 3 different implementations of Tasks and Handlers. First I will show the code for submitting a Task for processing, since it is the same for all implementations. Then, I will list the protocol for registering a handler, as well as a link to the implementation, for each implementation.

                                  So, to submit a Task:
                                  service.process(new TestTask1());
                                  Next, the registration of a handler and a link to the implementation

                                  1 ^st^ : [url http://reefedjib.org/Tasks-ParameterizedHandlerWithTaskClass.html]Using a parameterized handler class with a Task Class argument to the constructor
                                  <li> Classic approach: specify the Generic Type and the Task Class for dispatch filtering.
                                  service.addHandler(new Handler<TestTask1>(TestTask1.class) {
                                      public void handle(TestTask1 task) { System.out.println("task handled: " + task); }
                                  });
                                  2 ^nd^ : [url http://reefedjib.org/Tasks-InnerHandler.html]Using a non-static inner Handler class for each Task subclass
                                  <li> Avoids the need for a Generic Type, which is specified in the TestTask1 class specification. Requires instantiating the Task and the Handler.
                                  service.addHandler(new TestTask1().new Handler() { 
                                      public void handle(TestTask1 task) { System.out.println("task handled: " + task); }
                                  });
                                  3 ^rd^ : [url http://reefedjib.org/Tasks-AnonTaskClassHandler.html]Using an anonymous Task subclass and voluntarily overriding the handle method
                                  Note: The system does not statically force the user to implement the overridden method. A Runtime Exception will throw if an attempt to have the handler handle a Task occurs.
                                  <li> Avoids specifying the Generic Type and both the Task and Handler Classes. Not statically checked for the handle method implementation. Late binding solution.
                                  service.addHandler(new TestTask1() { 
                                      public void handle(TestTask1 task) { System.out.println("task handled: " + task); }
                                  });
                                  Here is sample output for all implementations:

                                  task handled: TestTask2
                                  task handled: TestTask1
                                  task handled: TestTask2
                                  task handled: TestTask2
                                  task handled: TestTask2
                                  task handled: TestTask1
                                  task handled: TestTask2
                                  task handled: TestTask2
                                  task handled: TestTask2
                                  task handled: TestTask1

                                  Edited by: reefedjib on Jun 18, 2011 3:36 PM