Issue Details (XML | Word | Printable)

Key: JRUBY-2799
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Major Major
Assignee: Thomas E Enebo
Reporter: Praki Prakash
Votes: 0
Watchers: 3
Operations

If you were logged in you would be able to see more operations.
JRuby

Need some mechanism to call masked java methods (initialize)

Created: 14/Jul/08 05:22 PM   Updated: 12/Jan/09 04:27 PM
Component/s: Java Integration
Affects Version/s: None
Fix Version/s: JRuby 1.1.6

Time Tracking:
Not Specified

Environment: Any


 Description  « Hide
I have some java classes which define a method named "initialize". These methods are expected to be called before the instance can be used. Ruby's initialize method hides the java instance method and I can never call that method. I haven't found any work around for this issue (_send_ invokes the Ruby's initialize method).

Is there any work around or possibility of a fix? This literally stopped me in my tracks while I was happily executing JRuby code until running into this issue.



 All   Comments   Work Log   Change History      Sort Order: Ascending order - Click to sort in descending order
Bill Dortch added a comment - 15/Jul/08 07:36 PM
There's a workaround using reflection, but it's really ugly. If your initialize method takes no args, it looks like this:
my_inst = MyClass.new(...)
init_method = my_inst.java_class.java_method :initialize
init_method.invoke(Java.java_to_ruby(my_instance))

I've been meaning for a while to implement a java_call method that works like this:

result = my_inst.java_call :my_method, 'an arg or', 2

Args and the result would be converted automatically.

Comments?


Praki Prakash added a comment - 15/Jul/08 11:17 PM
Thanks Bill. It's better than writing in straight Java and this is a good enough work around.

I think jruby should provide a standard mechanism that works in all cases (class and instance methods).


Jaroslaw Zabiello added a comment - 12/Aug/08 11:31 AM
This workaround does not work. I checked JRuby 1.1.2 and 1.1.3.

irb(main):046:0> pool.class
=> Java::ComDangaMemCached::SockIOPool

irb(main):047:0> init_method = pool.java_class.java_method :initialize
=> #<Java::JavaMethod/initialize()>

irb(main):048:0> init_method.invoke(Java.java_to_ruby(pool))
TypeError: invokee not a java object


Jaroslaw Zabiello added a comment - 13/Aug/08 10:26 PM
OK, guys. I have the solution.

class Java::JavaLang::Object
def java_call(method_name, *params)
jcls = self.java_class
m = jcls.declared_method(method_name, *params.map do |x|
x[0].to_s =~ /^[A-Z]/ ? eval("java.lang.#{x[0]}"): x[0]
end)
m.invoke self.java_object, *params.map{|x| Java.ruby_to_java(x[1])}
end
end

  1. Examples of usage:

Tescior.new.java_call :initialize
Tescior.new.java_call :initialize, [:String, "World!"]
Tescior.new.java_call :initialize, [:int, 7], [:Integer, 8]
Tescior.new.java_call :initialize, [:Integer, 9], [:Integer,10]

It is necessary to pass parameter types because knowing method name is not enough to pick up the methods. Java uses method overloading. JRuby does not distinguish between int and Integer. Look at third and forth method below.

public class Tescior {
public void initialize() { System.out.println("initialize is reserved for constructor in JRuby"); }
public void initialize(String name) { System.out.println("Hello " + name); }
public void initialize(Integer x, Integer y) { System.out.printf("%s + %s = %s\n", x,y,(x+y)); }
public void initialize(int x, Integer y) { System.out.printf("%s + %s = %sn", x,y,(x+y)); } }
}


Thomas E Enebo added a comment - 01/Dec/08 12:21 PM
I realy like the last commenters solution. I will write it in Java and change the name from 'java_call' to 'java_send' to mirror 'send' (makes it easier for Rubyists to remember).

Thomas E Enebo added a comment - 02/Dec/08 03:26 PM
We ended up fixing this differently than outlined above. The new synax is to use 'send' and to suffix your method name with $method. So:
public class Foo {
  public String initialize() { return "foo"; }
}

In Ruby:

Foo.new.send("initialize$method") # returns "foo"

Future versions JRuby will allow targeting specific signature methods through similiar name-mangling (though not for 1.1.6).


Thomas E Enebo added a comment - 02/Dec/08 03:49 PM
Oops. After the fact realization that if anyone tries to call 'initialize$method directly instead of via send we will call intiailize with the value of $method (bound to be nil). FAIL!

New method is __method as suffix. This has an extremely slight possibility of name conflict (very extremely), but it is directly callable:

public class Foo {
  public String initialize() { return "foo"; }
}

In Ruby:

initialize__method # returns "foo"