Javaでは、finalizeという名前の関数は「ファイナライザ」という特別な関数です。ファイナライザはクラスの後始末を行うために存在するが、いつ呼び出されるかわからない、また、呼び出されないこともあるらしく、「何かを積極的におこなう場所」ではありません。基本的に使用は推奨されてない、らしいです。
C#では同じような目的で「デストラクタ」があります。C#でデストラクタはクラス名に「~」をつけて宣言しますが、内部的には「Finalize」という関数名に変換されます。そのため、「Finalize」という関数名を使用すると、以下のような警告やエラーが出ます。
- 「void Finalize()」という関数を作成すると「デストラクタを宣言しようとしましたか?」警告が出ます。
- 「override void Finalize()」という関数を作成すると「Finalizeをオーバーライドしないでください。代わりにデストラクタを提供してください。」エラーが出ます。
何が問題か
XamarinバインドプロジェクトでJARをバインドする際、JARの中にファイナライザが含まれていると、Javaの「finalize」がC#の「Finalize」という関数名となってエラーが出てしまう、という問題が発生します。
本来あるべき姿としては、「Javaのfinalizeという関数名をC#のデストラクタに変換してほしい」のですが、そうはなりません。
Xamarinバインドプロジェクトでは、ビルド時エラーが発生した場合、「Metadata.xml」を編集することで、変換の方法を細かく指示することにより、エラーを回避することができます。それでは、今回、「Metadata.xml」でどのように指定すればよいか?ネットで検索したところ、いい方法はないらしい。
案1.消してしまえばいい説
ファイナライザは使用すべきでないのだから、消してしまっても問題ない、という考えのようです。
<remove-node path="/api/package[@name='your.package.name']/class[@name='Proper.ClassName']/method[@name='finalize' and count(parameter)=0]" />
案2.強引におきかえる派
Javaの
protected final void finalize()
という関数名は、通常なら
protected override sealed unsafe void Finalize()
に変換されてエラーになるが、無理やり
unsafe /**/ ~FooIterator()
というデストラクタにしています。なかなかすごい手です。
<attr path="/api/package[@name='com.company.api']/class[@name='FooIterator']/method[@name='finalize']" name="managedName">~FooIterator</attr>
<attr path="/api/package[@name='com.company.api']/class[@name='FooIterator']/method[@name='finalize']" name="visibility"></attr>
<attr path="/api/package[@name='com.company.api']/class[@name='FooIterator']/method[@name='finalize']" name="managedReturn">/**/</attr>
案3.自分で見つけた方法。
まずはビルドします。当然エラーになります。以下のようにエラー一覧が表示されます。
「エラー一覧」ウインドウからエラーが発生したダブルクリックしてエラー箇所を表示します。
Finalizeが関数名です。赤波線で「ここがエラーだよ」と教えてくれています。このソースコードを直接変更します。この例だと、たとえば今回はEventクラスのデストラクタですので、関数名をFinalizeから「~Event」に変更します。「protected override sealed」「void」は不要なので削除します。
このように変更後、「ビルド」を選択すれば、問題なくビルドできました。ただし「リビルド」や「クリーン」を選択してはいけません。「リビルト」を選択すると、デストラクタがもとのファイナライザに戻ってしまします。
どれがベストか?
- 実際は案1で問題になる事はないと思われます。
- しかし、本来あるべき姿としては、案2か案3です。案3はリビルドしたらエラーになるため、現実的には案2が最適ではないか思います。