Javaを書いていると、たまにこんなコードを書いてしまうことがあります。
Sample.doSomething();
でも実際には "doSomething()" はインスタンスメソッドだったりします。
当然コンパイルエラーになります。
普通なら
new Sample().doSomething();
や
sample.doSomething();
に修正します。
ですが、ふと思いました。
「コンパイラエラーを自動で直せないだろうか?」
もちろん、安全性は考えません。
思いついた変換
例えば、インスタンスメソッド
void doSomething() {
...
}
があったとします。
これに対して、staticで呼んでしまうコードを検知した場合は自動で
static void doSomething__AutoGenerated123(...) {
...
}
のような、絶対に競合しなさそうな名前のstaticメソッドを1回だけ生成します。
元のインスタンスメソッドは、そのstaticメソッドを呼ぶだけのラッパーに変更します。
さらに、
Sample.doSomething();
という「インスタンスメソッドをstaticで呼ぼうとしているコード」を検出したら、
Sample.doSomething__AutoGenerated123(...);
へ書き換えてしまいます。
コンパイルは通ります。
もちろん問題だらけ
実際には簡単ではありません。
例えば
- "this"
- インスタンスフィールド
- "super"
- オーバーライド
- ポリモーフィズム
などが絡むと、そのままstatic化はできません。
本来なら
this.value
を参照しているコードは、
self.value
のような形でインスタンスを受け取る必要があります。
つまり、
static void doSomething(Sample self)
のような形になるでしょう。
すると今度は、
「その "self" はどこから持ってくるんだ?」
という根本的な問題に突き当たります。
でも面白そう
実用性はかなり怪しいです。
正しく動く保証もありません。
ですが、
Javaソース全体を解析し、
「インスタンスメソッドをstaticで呼ぼうとしている場所」だけを検出して、
強引にコンパイルが通る形へ変換する
というツールは、技術的には作れそうな気がします。
もちろん「正しいプログラムを作る」ためのものではなく、
「壊れたコードを無理やりコンパイル可能な形へ変形する実験」
としてです。
ASTを書き換える遊びとして
JavaにはASTを扱えるライブラリがいくつもあります。
それらを使えば、
- メソッド宣言を追加する
- 呼び出し元を書き換える
- 必要なら引数を追加する
といった変換自体は実現できそうです。
「Javaのソースコードを書き換える」というテーマだけでも、結構面白い題材になりそうです。
おわりに
もちろん、この仕組みを本番環境で使いたいとは思いません。
ですが、
「コンパイルエラーを消すためだけに、ソースコードを大胆に書き換える」
という発想は、なかなかロマンがあります。
実用性はほぼゼロ。
でも、こういう"無茶な変換器"を考えてみる時間も、プログラミングの楽しさの一つなのかもしれません。