.NET Tools
注目のJava→C#コンバータを試用する

3.りすと亭の変換で遭遇した問題点 

(株)ピーデー
川俣 晶
2002/03/09
Page1 Page2 Page3 Page4 Page5 Page6

 以下、「りすと亭」を使った具体的なプロジェクトの変換作業において、遭遇した問題点についてまとめよう。遭遇した問題点はかなりの数に登るのだが、これらの中には、JLCAが構造的に持つ問題というよりも、JLCAがまだ開発途中のベータ1であるためと思われるものも少なくなかった。そこで以下では、単純なバグではないと思われる問題点を中心にまとめる。ただし読者の中には、手元にあるJavaのソース・コードを、すぐにでもJLCAベータ1でC#に変換したいという人がいるかもしれない。こうした読者にとっては、たとえ単純なバグであっても情報としては価値があるだろう。そこでこれらについては、参考情報として、「コラム:JLCAベータ1で発生した問題点(バグ編)」という別ページにまとめた。必要がある人はこちらも併せて参照されたい。

問題点(1):java.io.RandomAccessFileがサポートされない

 りすと亭では、テキスト・ファイルに追加書き込みをする必要があり、java.io.RandomAccessFileを使ってそれを実現する自作クラスAppendFileを作っていた。しかし、JLCAではこのクラスがサポートされないため、エラーの嵐になった。最初は、このクラスの中身を書き換えて、等価の実装を書き込もうと思ったが、.NET Frameworkでは標準的に追加モードでファイルを開くことが可能なので、追加モードでストリームをオープンするように呼び出し元のソースを書き直して、自作AppendFileクラスは捨ててしまった。製品版ではこの機能がサポートされることを期待したい。

問題点(2):java.lang.Runnableは全面書き換え

 java.lang.Runnableに対応するインターフェイスは存在せず、delegateに置き換えなければならない。これは手動で対処した。

問題点(3):エンコーディング名の非互換性

 すでに触れたとおり、文字のエンコーディング名に非互換の部分がある。以下にいくつかのJavaの方言名とIANA名との対応を紹介する。

Javaでのエンコーディング名 IANAでのエンコーディング名
8859_1 ISO-8859-1
SJIS Shift_JIS
JIS ISO-2022-JP
UTF8 UTF-8

 なお、Javaで使用できるエンコーディング名に関しては、風間一洋さんがJava Encodingsとして調査をまとめているので、自信がない場合はこちらを参照するとよいだろう。IANA登録名のリストはIANAのサイトにCHARACTER SETSとして置かれている。

問題点(4):java.lang.ThreadGroup

 すでに述べたように、java.lang.ThreadGroupに対応するクラスはなく、この点については手動で大幅に書き換えることになったが、完全に機能的に等価なものにはならなかった。

問題点(5):byte配列と文字列の変換

 りすと亭では、複数の文字エンコーディングを扱う関係上、文字列をbyte配列として保持し、必要に応じてエンコーディング名を指定して文字列に変換、あるいは逆方向の変換を多用していた。Javaでは、Stringクラスのコンストラクタでbyte配列から文字列への変換、String.getBytesメソッドで文字列からbyte配列への変換が容易にできる。しかし、この2つの機能はJLCAではサポートされていなかったので、等価なサポート・メソッドを自作して、これを呼び出すように手動で書き換えた。以下は作成したサポート・メソッドの一例である。

public static string SByteToString( sbyte [] array, string encodingName )
{
    return new String(array, 0, array.Length, System.Text.Encoding.GetEncoding(encodingName) );
}

問題点(6):アクセス・レベルの不一致

 publicとinternalのアクセス・レベルがかみ合わないソースが生成される場合があり、コンパイル・エラーを引き起こす場合があった。例えば以下がエラー・メッセージの一例である。

D:\w\Inet\ML31.NET\ML3Body.cs(346): Inconsistent accessibility: return type 'ML3TitleItem' is less accessible than method 'ML3TitleDB.get(int)'

問題点(7):インナー・クラスに挿入されるメソッド

 クラス内で宣言されたクラスには、以下のようなメソッドが自動的に挿入されていた。

private void InitBlock(MyInputQueue enclosingInstance)
{
    this.enclosingInstance = enclosingInstance;
}

 今回試した範囲内で、これが使われるケースはなかったので、何のために挿入されたものかははっきりしない。

問題点(8):java.io.FilenameFilterに対応するインターフェイスがない

 ファイルを検索する際に、Javaでは主にjava.io.FilenameFilterインターフェイスによって目的のファイルを識別するが、.NET Frameworkでは主にワイルド・カードを指定する。構造があまりにも違うので、手動で大幅な書き換えを要した。

問題点(9):ディレクトリとファイルの混乱

 java.io.Fileは内容としてファイルを示す場合と、ディレクトリを示す場合がある。それに対して、.NET Frameworkでは、ファイルはSystem.IO.FileInfo、ディレクトリはSystem.IO.DirectoryInfoで扱う。JLCAでは無条件でjava.io.FileをSystem.IO.FileInfoに変換してしまうようだが、ディレクトリを扱うことを意図している場合は、手動でSystem.IO.DirectoryInfoに書き換える必要がある。

問題点(10):sbyte型の符号

ar[5] = (sbyte) 0x81;

という変換結果のコードが、以下のエラーを起こした。

D:\w\Inet\ML31.NET\Base64.cs(182): Constant value '129' cannot be converted to a 'sbyte' (use 'unchecked' syntax to override)

 数値の符号の有無と、16進数値の扱い、オーバーフロー・チェックの関係により、このような単純なキャストがエラーになるケースがある。そこで必要とされる機能は何かを考えたうえで、ケース・バイ・ケースの手動修正が必要であろう。

問題点(11):サポート専用クラス

 これは問題点ではないのだが、簡単には互換性を維持できない機能を収めるSupportClass.csというソース・ファイルが自動生成されていた。以下は、りすと亭変換時に生成されたサポートクラスに含まれる主要なクラス、メソッド、フィールドである。

  • public static int URShift(int number, int bits)
  • public static int URShift(int number, long bits)
  • public static long URShift(long number, int bits)
  • public static long URShift(long number, long bits)
  • public static byte[] ToByteArray(sbyte[] sbyteArray)
  • public static sbyte[] ToSByteArray(byte[] byteArray)
  • public class Tokenizer
  • public static long FileLength(System.IO.FileInfo file)
  • static public System.Random Random = new System.Random();

 シフト演算子関係の置き換え用らしきメソッド群、Javaではbyteが符号付きである関係上、バイト配列の符号の有無を相互変換するメソッド群、Java互換のStringTokenizer、微妙に挙動が違うファイル・サイズ取得機能、乱数の提供機能などが並んでいる。

問題点(12):未対応のSimpleDateFormat

 java.text.SimpleDateFormatなどの日付/時刻関係のクラスは、今回評価したベータ1ではまだ未対応なので(製品版では対応されると思われる)、手動での書き換えが必要だった。日付/時刻関係のクラスについては、Javaと.NET Frameworkではかなり構造が違っており、大幅な書き換えを要した。

問題点(13):tickに挿入される巨大定数

 Tickを扱うコードに巨大な定数が挿入される。

long target = (d.Ticks - 621355968000000000) / 10000;

 おそらく、tick値の起点とスケールが違うために挿入されているものだと思うが、tickの絶対値に意味を持たせていない(つまりtickの差分しか必要としない)場合は、“- 621355968000000000”の部分を削除してしまっても構わないだろう。

問題点(14):サポートされない例外

 java.io.UnsupportedEncodingException例外はJLCAではサポートされていないので、手動で対応が必要であった。とはいえ、多くの使い方ではエンコーディング名はサポートされていると分かっている名前を書くわけで、いちいちこの例外をキャッチするコードを書く必然性は薄い。それでもJava上でこの例外をキャッチしていたのは、形式的にそれが必要とされていたからで、C#上ではこの例外をキャッチするコードを取り除いてしまっても構わない事例が多いのではないかと思う。

問題点(15):ホスト名を取得する

 変換されたソースで、getLocalHostメソッドがないという“TODO”がコメントに挿入されていた。プログラムを実行しているそのマシンのホスト名とIPアドレスを調べる流儀が異なっているので、単純な変換では済まないようである。Javaでは、まずIPアドレスを取得してからこれをホスト名に変換するが、.NET FrameworkではまずSystem.Net.Dns.GetHostNameメソッドでホスト名を取得してからこれをIPアドレスに変換する。この違いを正しく反映させるためには、手動の書き換えを要した。

問題点(16):謎のコード

 謎の変換も見られた。以下が変換元ソースである。

if( out.checkError() ) return;

 以下が変換結果である。

if (false)
return ;

 おそらくout.checkError()に相当するメソッドがないので、常にfalseに変換しているのだろうが、無意味などころか、コンパイラがいちいち警告してくるので、手動で取り除いた。

問題点(17):付きすぎるvirtual

 Javaのメソッドはデフォルトでvirtualである。そのため何も考えないで書かれたクラスをJLCAで変換すると、virtualメソッドのオンパレードになる。それで実行できないわけではないが、後々のトラブルを防止するには、不要なvirtualキーワードは取り除いておいた方がよいだろう。

問題点(18):動的なクラスの判定

 定数値までは面倒を見てくれないため、動的にクラスの型をチェックするコードが正常に動作していないケースがあった。以下は定数値の中にJavaクラス名が残ってしまった例だ。java.lang.StringはSystem.Stringに変換されているのだが、定数値の中の名前は変換されていないことが分かるだろう。

1: System.Type c = o.GetType();
2: System.String className = c.FullName;
3: if (className.Equals("java.lang.String"))
4: {
5:     System.String r = (System.String) headers.get(item);
6:     if (r == null)
7:         return "";
8:     return r;
9: }

 もっともこの程度なら、こんなに回りくどいことをせずともinstanceof/is演算子で判定すればそれで済む話なのだが。

問題点(19):Readメソッドの戻り値の問題

 各種入力クラスにあるread/Readメソッドの戻り値の意味が異なっている。JavaのreadメソッドはEOFのとき−1になるが、.NET FrameworkのReadメソッドでは0になる。JLCAではこの違いを書き換えてくれないので、永遠にEOFを検出しないソースができてしまうことがある。以下がその一例である。

int r = fis.Read(t,0,t.Length);
if (r == -1) break;

関連リンク
Java Encodingsのページ
CHARACTER SETSのページ
 

 INDEX
  [.NET Tools]
  注目のJava→C#コンバータを試用する
     1.インストールと実行
     2.変換の内容と品質
   3.りすと亭の変換で遭遇した問題点
     4.稼働したソースと結論
         コラム:JLCAベータ1で発生した問題点(バグ編)
 
インデックス・ページヘ  「.NET Tools」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間