Friday, September 18, 2009

Creating anonymous classes from Java interfaces in JRuby

Today I tried to create an anonymous class from an inteface in JRuby. After a little bit of experimenting a collegue showed me that I need to alter the singelton class of an instance of the interface in order to implement a method. Here I implement the Callable-Interface:

callable = Java::java.util.concurrent.Callable.new 
class << callable
  def call
    # implements method 'call' of the Callable-Interface
  end
end

So I tried this:

require 'java'
include_class 'java.util.concurrent.Callable'
include_class 'java.util.concurrent.Executors'

msg = "from thread"

callable = Java::java.util.concurrent.Callable.new 
class << callable
  def call
    msg
  end
end

executor_service = Executors.newFixedThreadPool 1
future = executor_service.submit callable
puts "hello #{future.get}"
# -> undefined local variable or method `msg'

For my suprise, a variable defined in the outer context was invisible for the implemented method. I expected that it would behave just like a Ruby block or a Java anonymous class which can "see" the outer defined variables.

We found a workaround for this problem by calling a method on the instance that initializes a instance variable with the needed object:

require 'java'
include_class 'java.util.concurrent.Callable'
include_class 'java.util.concurrent.Executors'

msg = "from thread"

callable = Java::java.util.concurrent.Callable.new 
class << callable
  def init(msg)
    @msg = msg
  end
  def call
    @msg
  end
end
callable.init msg

executor_service = Executors.newFixedThreadPool 1
future = executor_service.submit callable
puts "hello #{future.get}"
# -> hello from thread
Please let me know if there a more elegant way to accomplish this!

3 comments:

  1. Using keywords such as `class' and `def' change the current scope - so your `msg' varialbe is no longer local within your #init and #call methods. Said another way: `class' and `def' can't close over outer variables.

    What you wan't is some way to define #init and #call as a closure, which can capture `msg'. For that, you'd wan't to use Module#define_method. For further details, check out my two answers to this StackOverflow question:
    http://stackoverflow.com/questions/2495550/define-a-method-that-is-a-closure-in-ruby/2495650#2495650

    Cheers,
    -Charles

    ReplyDelete
  2. Nice one, ran into the same problem as well with anonymous classes and interfaces. Good reference here too:

    Anonymous class implements interface

    ReplyDelete
  3. https://www.ruby-forum.com/topic/188599

    Maybe this link will solve your prob.

    ReplyDelete