简体   繁体   中英

jruby + no public constructor

Using JRuby 1.6.0RC1

I've got a java file like

package com.foo.bar

public class Foo
{
Foo(String baz){}
}

If, in jruby, I do

com.foo.bar.Foo.new "foo"

then I get

TypeError: no public constructors for Java::ComFooBar::Foo

Reading http://jira.codehaus.org/browse/JRUBY-5009 makes me thing this is WAD, but how do I get around the problem without altering the java file?

Subclassing Foo and then instantiating I get a different error:

ArgumentError: Constructor invocation failed: tried to access method com.foo.bar.Foo.(Ljava/lang/String;)V from class org.jruby.proxy.com.foo.bar.Foo$Proxy0

EDIT:

Got it to work through help from Headius on IRC. The following works, but could possibly be more intelligent:

 def package_local_constructor klass,*values
    constructors = klass.java_class.declared_constructors
    constructors.each do |c|
      c.accessible = true
      begin
        return c.new_instance(*values).to_java
      rescue TypeError 
        false
      end
    end
    raise TypeError,"found no matching constructor for " + klass.to_s + "(" + value.class + ")"
  end

There indeed is no public constructor for that. The constructor is package level.

How do other Java classes outside the package com.foo.bar acquire objects of this type? It may be there is already a factory in that package that produces this class by calling the package-scoped constructor, and that you could call from JRuby.

If not, you could make a public factory class in that package, possibly in Java, possibly in Ruby, and call this constructor from there.

You might also be able to monkey-patch to add a ruby-accessible constructor or factory method, without having to modify the Java source.

That's because the constructor is has package level access.

You could try to define your ruby class in the same package as the foo class.

See: Assigning a Java package to a JRuby class

In Java you can use the reflection API to do something like:

Constructor constructor = MyClass.class.getConstructor(Class ... paramTypes);
constructor.setAccessible(true);
MyClass myClass = (MyClass)constructor.newInstance(Object ... args);

Not sure you can do that in JRuby, but I'd imagine you could.

There's an oracle guide to this: http://download.oracle.com/javase/tutorial/reflect/member/ctorInstance.html

Guess the only fixes are the one you proposed, or "remove your initializer from the ruby class" (which may be a bug in jruby--shouldn't it call its ancestor no matter what?) or "make the java class initializer protected access" [I'm not sure why jruby disdains package level so much].

http://betterlogic.com/roger/2011/05/javajavamirah-woe/comment-page-1/#comment-5034

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM