From: "Andrew S. Townley" Date: 2009-04-27T19:56:33+09:00 Subject: Re: JRuby constructor issue or just me? Going to combine a few messages in one here: On Mon, 2009-04-27 at 13:09 +0900, Gennady Bystritsky wrote: > In Ruby, "super()" is different from just "super". The former will > invoke a superclass' constructor with no parameters, while the latter > will call it with the same number of parameters as was in "new" call. Thanks. That's exactly the behavior I wanted. I wanted to call the empty constructor explicitly. On Mon, 2009-04-27 at 13:25 +0900, Nick Sieger wrote: > Your initialize method requires at least a "count" parameter, but > you're not passing any. Do you mean to default it to something? > > foo = Foo.new(42) > > should work. Good catch, Nick. Test case error. I was incrementally trying to build up to the exact scenario that got the same error as my actual code. Unfortunately, I stopped when I got the error, rather than actually believing the compiler. The original code didn't have this problem. On Mon, 2009-04-27 at 10:29 +0900, Charles Oliver Nutter wrote: > Andrew S. Townley wrote: > > $ cat $HOME/output.txt > > nene$ c:/devel/src/jruby/bin/jruby -v > > jruby 1.3.0 (ruby 1.8.6p287) (2009-04-26 r6586) (Java HotSpot(TM) Client VM 1.6.0_13) [x86-java] > > nene$ c:/devel/src/jruby/bin/jruby jbutton.rb > > jbutton.rb:13: wrong # of arguments(0 for 1) (ArgumentError) > > Constructors can be a little fiddly from within a Ruby class that > extends a Java class, due to some complicated gymnastics. If at all > possible you'd be better off aggregating the button, but if you must > extend, extend with the same constructor style and just call "super" > > We want to improve how this works, but the extension code needs a > rewrite before that can happen. Contributing broken specs for cases like > this that ought to "just work" would help us too. Thanks for the info here, Charlie. Unfortunately, I was barking up the wrong tree due to the message I got from jruby. I haven't been able to duplicate it in a simple case, but the code that caused the problem was actually this: 28 class Button < JButton 29 def initialize(*args, &block) 30 super() 31 if args.length > 0 && args[0].is_a?(Action) 32 # action = args[0] 33 self.action_command = action.action_id 34 if action.status_text.nil? 35 self.tool_tip_text = action.label 36 else 37 self.tool_tip_text = action.status_text 38 end 39 self.text = action.label Originally, I didn't have the reference in line 32 because I'd copied this code from another method where I was using aggregation instead of inheritance, which produced the following error: excalibur$ jruby actiontest.rb ..[path elided]/button_peer.rb:33:in `initialize': wrong # of arguments(0 for 2) (ArgumentError) from actiontest.rb:46:in `initialize' from actiontest.rb:58 The call in actiontest.rb looks like this: 45 back = BackAction.new 46 b1 = Button.new(back) Since I hadn't done the subclassing before, and the error was complaining about the constructor call, that's where I started looking. I couldn't figure out why the line number in the original error refers to the first time the JButton instance was used, however. Since Nick pointed out that the empty call to JButton() actually works via 'super()', I went back to ensure that I wasn't making the same mistake in the original code. This morning, I noticed that I had inadvertently lost the reference to the named action variable in the refactoring. After adding line #32, everything works swimmingly. This is actually the kind of thing I was talking about when I said I was having to translate JRuby error messages. Another case was when I was trying to give a derived AbstractAction sub-class, e.g.: 27 class ToggleAction < AbstractAction 28 def initialize(action) 29 super("Toggle Action State") 30 @action = action 31 end 32 33 def actionPerformed(event) 34 puts "perf" 35 @action.enabled = !@action.enabled 36 end 37 end Originally, since JRuby does method translation for action_performed to actionPerformed, I'd implemented the class like this: 27 class ToggleAction < AbstractAction 28 def initialize(action) 29 super("Toggle Action State") 30 @action = action 31 end 32 33 def action_performed(event) 34 puts "perf" 35 @action.enabled = !@action.enabled 36 end 37 end This gives the usefully obtuse (but totally accurate stack trace): excalibur$ jruby actiontest.rb Exception in thread "AWT-EventQueue-0" java.lang.AbstractMethodError: org.jruby.proxy.javax.swing.AbstractAction$Proxy2.actionPerformed(Ljava/awt/event/ActionEvent;)V at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995) at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318) at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387) at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242) at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236) at java.awt.Component.processMouseEvent(Component.java:6134) at javax.swing.JComponent.processMouseEvent(JComponent.java:3265) at java.awt.Component.processEvent(Component.java:5899) at java.awt.Container.processEvent(Container.java:2023) at java.awt.Component.dispatchEventImpl(Component.java:4501) at java.awt.Container.dispatchEventImpl(Container.java:2081) at java.awt.Component.dispatchEvent(Component.java:4331) at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4301) at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3965) at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3895) at java.awt.Container.dispatchEventImpl(Container.java:2067) at java.awt.Window.dispatchEventImpl(Window.java:2458) at java.awt.Component.dispatchEvent(Component.java:4331) at java.awt.EventQueue.dispatchEvent(EventQueue.java:599) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161) at java.awt.EventDispatchThread.run(EventDispatchThread.java:122) Admittedly, this one didn't take me as long to figure out the root cause, but it still wasn't very helpful in identifying the actual cause. Given that the behavior of "name mapping" isn't 100% consistent (that it does it during calls, but not during definition), it might actually cause problems for more people than just me. I know that JRuby isn't the only implementation that does this (so does IronRuby), but it might be something to put on the "todo" list to at least provide some kind of warning or more automatic fixup. While convenient, and certainly "more ruby-like", I'm not really sure that doing all of the automagic method translation stuff is actually all that necessary, and, since it's difficult to do for all cases without adding even more overhead, maybe might just be simpler to avoid. I'm sure most people would disagree with me, but really, how much more of a PITA is it to do 'rubyobj.addActionListener xyzzy' than 'rubyobj.add_action_listener xyzzy'? At least you've some visual clue that you're calling 'native' code here, and you don't have to worry about knowing when the interpreter's going to "do what you mean" vs. "do what you actually said". Overloaded methods are another kettle of fish... :) Anyway, the problem's solved now thanks to your collective help. Aggregation isn't an option in this particular case (even though that's my default approach), because what I need is a UI peer class that does some translation between an abstract toolkit's concepts of buttons, actions, etc. and their "native" counterparts. All the peers need to be 'native' so that they can be passed around to the appropriate classes where necessary in a consistent way vs. being ignored 99% of the time by the rest of the application code. Glad it did turn out to be just me, but a more accurate error message would be nice. Actually, as I was going to write the rest, I just figured out why I hit the problem. Action is an accessor of JButton. Since I used the same name, silent collision, the compiler's happy, but it's obviously still somewhat confused. Not really sure what message I'd expect, but I still think the current message is misleading. At least now I understand what the issue was. Cheers, ast -- Andrew S. Townley http://atownley.org