Ruby to Java 変換

JRubyからJava呼び出しの際にどんなオブジェクトが渡るかテスト

public class Convert {
	public static void print(Object obj){
		System.out.println(obj.getClass() +":"+ obj);
	}
}
include Java

def conv obj
  Java::Convert.print obj
end

conv 1
conv 1.1
conv "a"
conv true
conv 1 << 128
conv :a
conv [4,5,6]
conv({:a=>1, "b"=>0.1})
puts

conv 1..4
conv /.*/
conv proc{}
require 'set'; conv Set.new []
conv 1.class
conv Java
begin
  1.java_class
rescue => e
  conv e
end
puts

conv Dir.new "."
conv (f=File.new("conv.rb")); f.close
conv [9,8].to_java
conv [9,8].to_java :int
  • 結果
class java.lang.Long:1
class java.lang.Double:1.1
class java.lang.String:a
class java.lang.Boolean:true
class java.math.BigInteger:340282366920938463463374607431768211456
class org.jruby.RubySymbol:a
class org.jruby.RubyArray:456
class org.jruby.RubyHash:a1b0.1

class org.jruby.RubyRange:1..4
class org.jruby.RubyRegexp:(?-mix:.*)
class org.jruby.RubyProc:#<Proc:0x257807a@conv.rb:19>
class org.jruby.RubyObject:#<Set:0x176f5261>
class org.jruby.RubyClass:Fixnum
class org.jruby.RubyModule:Java
class org.jruby.RubyNoMethodError:undefined method `java_class' for 1:Fixnum

class org.jruby.RubyDir:#<Dir:0x552a66ea>
class org.jruby.RubyFile:RubyFile(conv.rb, 1, 4)
class [Ljava.lang.Object;:[Ljava.lang.Object;@174323d5
class [I:[I@637050f5

JRubyからJavaにclassオブジェクトを渡す

JRubyからJavaにclassオブジェクトを渡すとどの型になるのか試してみた。


Javaのコードは単純にclass文字列表現をprint

public class ClassSample{
	public static void print(Class clazz){
		System.out.println(clazz.toString());
	}
}

JRubyではJavaのビルトインクラスとJRubyで定義したクラス(継承ありなし)のclassオブジェクトをJavaに渡す

include Java

Java::ClassSample.print java.util.Date.class
Java::ClassSample.print java.util.Date.java_class

class ClassA; end
Java::ClassSample.print ClassA.class
begin
  Java::ClassSample.print ClassA.java_class
rescue => e
  p e
end

java_import 'java.util.HashMap'
class ClassB < HashMap ; end
Java::ClassSample.print ClassB.class
Java::ClassSample.print ClassB.java_class

その結果

class org.jruby.RubyClass
class java.util.Date
class org.jruby.RubyClass
#<NoMethodError: undefined method `java_class' for ClassA:Class>
class org.jruby.RubyClass
class java.util.HashMap

ClassBがHashMapになるのが意外

Ruby to Java 変換表

JRubyからJavaのメソッドを呼ぶ場合、メソッドの引数は自動で型変換される。
Javaのメソッドの引数を具体的な型で定義したとき、Rubyのどの型を受け入れられるか調べた。
(以下のようなメソッドをJRubyから引数に様々なものを入れて呼び出してみた)

	public static void i(int i){
		System.out.println(i);
	}

プリミティブ型

Java引数型 Ruby -> Java
boolean nil/false -> false, true -> true
char nil -> '\0', Fixnum/Float -> char
byte nil -> 0, Fixnum/Float -> byte
short nil -> 0, Fixnum/Float -> short
int nil -> 0, Fixnum/Float -> int
long nil -> 0, Fixnum/Float -> long
float nil -> 0.0, Fixnum/Bignum/Float -> float
double nil -> 0.0, Fixnum/Bignum/Float -> double

Wrapperクラスもnil -> nullになるほかは同じだった。

BigIntegerとString

Java引数型 Ruby -> Java
BigInteger Fixnum/Bignum/Float -> BigInteger
String String/Symbol -> String

Number型、BigDecimal型は型変換されずダメだった。(nilのみ受け入れ)

Collection

Java引数型 Ruby -> Java
List Array -> org.jruby.RubyArray(implements List)
Collection Array -> org.jruby.RubyArray(implements Collection)
Iterable Array -> org.jruby.RubyArray(implements Iterable)
Map Hash -> org.jruby.RubyHash(implements Map)

RubyArrayはList,Collection,Iterableを実装している。
ArrayList, HashMapなどの具象クラスは型変換されない。
(RubyArrayはArrayListではないので当然か)


Set, HashSetはダメだった。

配列

Java引数型 Ruby -> Java
Object[] [1,2]/[1,2].to_java -> Object[](中身はLong)
int[] [1,2]/[1,2].to_java :int -> int[]
Long[] [1,2]/[1,2].to_java :Long -> Long[]


一般的に使うものはわりとよく変換できてるみたい。

Servlet 3.0のアノテーション

@WebServletアノテーションを使うとweb.xmlいらないということで試してみた。
Tomcat 7.0.2で実験

以下のコードをwebapps/sample/WEB-INF/classes/sampleに置いてコンパイル

package sample;

import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet("/app/*")
public class SampleServlet extends HttpServlet {
	public void doGet(HttpServletRequest req, HttpServletResponse res) throws java.io.IOException{
		res.getWriter().print("testtest");
	}
}

これでhttp://localhost:8080/sample/app/a にアクセスでうまくいった〜


HttpServletの継承が必要なのはちょっとめんどいかも。
このコードをJRubyで書いてjrubyc --javaJavaコードに変換できるかなあと思ったけど無理だった。
JRubyJavaインテグレーションにあらためて限界を感じた・・・

自動的に閉じるJavaファイルストリーム

JRubyで外部のJavaライブラリと連携とかするとき、RubyのFileクラスのopenのように自動的に閉じるFileInputStream、FileOutputStreamがほしいので作ってみた。
わりと簡単に作れる

include Java
class FileInput
  def self.open(file, &block)
    raise "no block given" unless block_given?
    begin
      input = java.io.FileInputStream.new(file)
      yield input
    ensure
      input.close unless input.nil?
    end
  end
end

class FileOutput
  def self.open(file, &block)
    raise "no block given" unless block_given?
    begin
      output = java.io.FileOutputStream.new(file)
      yield output
    ensure
      output.close unless output.nil?
    end
  end
end

つかうときはFileOutput.open("filepath"){|out| ... }みたいな感じで

シーケンス図のリバース

シーケンス図をソースコードからリバースして作成するツールでいいのがないか探してみたけど、SoyatecのeUML2ってのがなかなかよさそう。
http://www.soyatec.com/euml2/


Trace2UMLやUMLGraphもソースコードからじゃないけど手軽にテキストベースでUML定義できたりするみたい