2011年08月03日

IntelliJ IDEAのJava→Scalaコード自動変換を試す

IntelliJ IDEAにはJavaのコードをScalaのコードに自動で変換する機能が付いている。

プロジェクトの中にJavaのクラスを作って、Refactor→Convert Java File To Scalaで変換ができる。また、プロジェクト中のJavaのコードの一部をコピってScalaのコードの中にペーストすると、Scalaのコードに自動変換した上で貼り付けてくれたりもする。

これをScala入門者が困った時用の機能として勧めて良いものか確認したかったので、軽く調査してみる。結論として、それなりに救ってくれそう。

以下、IntelliJ IDEAにJava→Scala変換してもらった結果。

==================================================
【普通の文字列】

// 変換前
String str = "文字列";

  ↓↓↓

// 変換後
var str: String = "文字列"

普通に変換できた。

==================================================
【finalな文字列】

// 変換前
final String str = "文字列";

  ↓↓↓

// 変換後
val str: String = "文字列"

finalの場合はvalにしてくれた。偉い。

==================================================
【普通のメソッド】

// 変換前
public int sum(int i, int j) {
  return i + j;
}

  ↓↓↓

// 変換後
def sum(i: Int, j: Int): Int = {
  return i + j
}

OK。

==================================================
【finalメソッド】

// 変換前
final protected int sum(int i, int j) {
  return i + j;
}

  ↓↓↓

// 変換後
protected final def sum(i: Int, j: Int): Int = {
  return i + j
}

普通に変換できた。

==================================================
【普通のクラス】

// 変換前
class Sample {
  public int sum(int i, int j) {
    return i + j;
  }
}

// 変換後
class Sample {
  def sum(i: Int, j: Int): Int = {
    return i + j
  }
}

普通に変換できた。

==================================================
【staticを含んだクラス】

// 変換前
class Sample {
  public static String STR = "文字列";
}

  ↓↓↓

// 変換後
object Sample {
  var STR: String = "文字列"
}

objectになった! ちょっと感動。

==================================================
【static「も」含んだクラス】

// 変換前
class Sample {
    int i = 0;
    static int j = 0;
}

  ↓↓↓

// 変換後
object Sample {
  var j: Int = 0
}
class Sample {
  var i: Int = 0
}

classとobjectに分裂した!

==================================================
【三項演算子】

// 変換前
return i > j ? i : j;

  ↓↓↓

// 変換後
return if (i > j) i else j

if〜elseになった。

==================================================
【for文】

// 変換前
for (int i = 0; i < 10; i++) {
    System.out.println(i);
}

  ↓↓↓

// 変換後
{
  var i: Int = 0
  while (i < 10) {
    {
      System.out.println(i)
    }
    ({
      i += 1; i
    })
  }
}

頑張った感が溢れている。確かにこういうことではあるか。

==================================================
【拡張for文】

// 変換前
String[] args = {"foo", "bar", "baz"};
for( String str: args ) {
  System.out.println(str);
}

  ↓↓↓

// 変換後
var args: Array[String] = Array("foo", "bar", "baz")
for (str <- args) {
  System.out.println(str)
}

こっちは普通に変換できた。

==================================================
【インクリメント】

// 変換前
i++;

  ↓↓↓

// 変換後
({
  i += 1; i
})

う、うん。よく頑張ったね。

==================================================
【break】

// 変換前
int i = 0;
while (true) {
  if (i > 100) break;
  i += 1;
}

  ↓↓↓

// 変換後
var i: Int = 0
while (true) {
  if (i > 100) break //todo: break is not supported
  i += 1
}

breakはnot supportedだと言われてしまった。

==================================================
【continue】

// 変換前
int i = 0;
while (i < 100) {
  if (i % 2 == 0)
    continue;
  i += 1;
}

  ↓↓↓

// 変換後
var i: Int = 0
while (i < 100) {
  if (i % 2 == 0) continue //todo: continue is not supported
  i += 1
}

continueもnot supportedと言われてしまった。

==================================================
【switch】

// 変換前
int i = 2;
switch (i) {
  case 1:
    System.out.println("単数");
    break;
  default:
    System.out.println("複数");
    break;
}

  ↓↓↓

// 変換後
var i: Int = 2
i match {
  case 1 =>
    System.out.println("単数")
    break //todo: break is not supported
  case _ =>
    System.out.println("複数")
    break //todo: break is not supported
}

breakがnot supportedだけど、matchに変換はしてくれている。

==================================================
【コンストラクタを持つクラス】

// 変換前
public class Sample {
  public Sample(int i) { }
}

  ↓↓↓

// 変換後
class Sample {
  def this(i: Int) {
    this ()
  }
}

なるほど、this()を呼んでコンパイル通るようにしたか。

==================================================
【コンストラクタを複数持つクラス】

// 変換前
public class Sample {
  private Sample() {}
  public Sample(int i) {}
}

  ↓↓↓

// 変換後
class Sample {
  private def this() {
    this ()
  }
  def this(i: Int) {
    this ()
  }
}

基本コンストラクタを作ってはくれないらしい。

==================================================
【インターフェース】

// 変換前
interface Interface1 {
  public void foo();
}
class Sample implements Interface1 {
  public void foo() { /* 実装 */ }
}

  ↓↓↓

// 変換後
abstract trait Interface1 {
  def foo: Unit
}
class Sample extends Interface1 {
  def foo: Unit = {
  }
}

インターフェースはabstrct traitになるらしい。

==================================================
【継承】

// 変換前
class Child {
  public Child(int i, int j) {
    /** 処理 */
  }
}
class Parent extends Child {
  public Parent(int i, int j) {
    super(i, j);
  }
}

  ↓↓↓

// 変換後
class Child {
  def this(i: Int, j: Int) {
    this ()
  }
}
class Parent extends Child {
  def this(i: Int, j: Int) {
    this ()
    `super`(i, j)
  }
}

基本コンストラクタとsuperあたりは弱いっぽい。

==================================================
【equals】

// 変換前
"abc".equals("def");

  ↓↓↓

// 変換後
"abc" == "def"

ちゃんとequalsを==に変換してくれた。偉い。

==================================================
【.class】

// 変換前
Class cls = String.class;

  ↓↓↓

// 変換後
var cls: Class[_] = classOf[String]

classOfに変換してくれた。偉い。

==================================================
【キャスト】

// 変換前
ArrayList<String> arrayList = new ArrayList<String>();
List<String> list = (List<String>) arrayList;

  ↓↓↓

// 変換後
var arrayList: ArrayList[String] = new ArrayList[String]
var list: List[String] = arrayList.asInstanceOf[List[String]]

asInstanceOfに変換してくれた。

==================================================
【try 〜 catch】

// 変換前
try {
  // 処理
} catch (Exception e) {
  e.printStackTrace();
} finally {
  // 処理
}

  ↓↓↓

// 変換後
try {
}
catch {
  case e: Exception => {
    e.printStackTrace
  }
}
finally {
}

普通に変換できた。

==================================================
【volatile】

// 変換前
volatile String str;

  ↓↓↓

// 変換後
@volatile
var str: String = null

volatileまでサポートしているとは……

==================================================
【native】

// 変換前
native void foo();

  ↓↓↓

// 変換後
@native
def foo: Unit

volatileが大丈夫なんだから、もちろんnativeも大丈夫。

==================================================

というわけで、けっこうなんとかしてくれることが分かった。

これなら「Javaではこう書くんだけど、Scalaではどう書けばいいんだろう?」と悩んだ時に、1つの情報提供元として役だってくれそうな気もする。