はじめに
コード補完に並ぶIDEならではの機能、リファクタリングについてです。Android Studioの元になったIntelliJ IDEAは、元々リファクタリングツール(IntelliJ Renamer)から発展してきたIDEのため、リファクタリング機能に関しては定評があります。
こちらの予想のナナメ上をいく精度でリファクタリングを行うため、人によっては「神ががっている」と思う人もいれば、行きすぎとも思える精度のため「何これ?わけわからん」と思う人と受け取りかたもさまざまです。
要するにクセがあるわけですが、せっかくの高性能リファクタリングを眠らせておくのももったいないので、今回の解説がみなさんの理解の助けになれば幸いです。
リファクタリングの基本
第8回 でも紹介しましたが、Android Studioにとって「Projectツールウィンドウ」で行うファイル操作のほぼすべてがリファクタリングになります(例外は「新規作成」くらいです) 。
ファイル操作の対象がJavaのソースコードである無しに関わりなく、リファクタリングとして機能します。ファイルの移動やコピーは、カット("Cut ") /コピー("Copy ") /ペースト("Paste ")やマウス操作で代用できるのであまり意識することが無いのですが、メニュー操作する場合は「Refactor」メニューから該当のコマンドを実行することになります。
別にリファクタリングしなくてもいい場合
Javaのソースコードに限らず、どんなファイルでもリファクタリング対象にするのは便利な反面、ありがた迷惑な時もあります。
「やりたいのは、リファクタリングではなく単なるファイル操作なんだ」という場合は、意固地にAndroid Studio上で操作を行わず、素直にOS付属のファイラ(ExplorerやFinder)で作業したほうが早くて簡単です。それを見越してか「Projectツールウィンドウ」のコンテキストメニューには "Reveal in Finder "や"Show in Explorer " というコマンドがあります(詳細は第8回 を参照) 。
または「Terminalツールウィンドウ」で操作してしまうのも一興です。
Android Studioの外からプロジェクト内のファイル操作をした場合、しばらくするとその変更結果がAndroid Studioに反映されます。即時で反映したい場合は、Fileメニューかツールバーの「 "Synchronize "」を実行してください。
Javaのリファクタリングは主にエディタ上で行います。対象箇所にカーソルをあてて、メニューバーの「Refactor」からお望みのリファクタリングコマンドを実行します。キーストロークは大差ありませんが、リファクタリングに関しては専用のクイックリスト のようなコマンド "Refactor This... " があります。
図1 「 Refactor This...」の実行例
実行できるリファクタリングコマンドはコンテキスト依存で絞られるますが、それでもコマンドは数多くあります。なので比較的よく使うであろうリネームや抽出系("Extract ~ ")あたりはショートカットキーを覚えてしまいましょう。
リファクタリングの実演
では実際にリファクタリングを行ってみます。おそらく最も使うであろうリネーム("Rename... ")を例に進めます。
その前に大事な事をひとつ。Android StudioにはEclipseのクイックフィックス(Ctrl+1
)やクイックアシスト(Ctrl+2
)のような万能コマンドはありません。やりたいこと(コマンド)をハッキリ指示しないと何もしてくれません。
一応、インテンション(Alt+Enter
)に、いくつかリファクタリングを促す候補がでますがちょっとしたヒントでしかなく、やりたい事は利用者が明示的に指示する必要があります 。このあたりの不親切さ/控えめさはAndroid Studioの特徴のひとつなので慣れるしかありません。
本題に戻りリネームを実演してみます。エディタ上でカーソルをリネームしたい対象に置き "Rename... " 実行します。大抵の場合、その場でリネームする候補が展開されます。
図2 "Rename..."の実行例
リネームの対象になり得るのはたくさんあります。ざっと挙げただけでも、以下の通りです。
変数名(ローカル変数、インスタンス変数、クラス変数)
引数名
クラス名、インターフェイス名
パッケージ名
リネームを指示する場所と対処によっては、図3 のように「継承元をリネームするのか」それとも「その場のものをリネームするのか」と問い合わせてきます。
図3 メソッドのリネーム例
意外かも知れませんが、リファクタリングはJavaのソースコードだけではなく、リソースファイル(XMLファイル)に対しても有効です。たとえば、次の対象もリネームできます。
「属性値の参照関係」とは、たとえば、とあるXMLタグの属性値で参照している@string/<ref_name>
の<ref_name>
をリネームすると、それを宣言しているstring.xml
の該当箇所もリネームする事を指します。
こちらの予想に反していろんなところでリネームに反応します。「 これリネームできたらいいな」と思ったらダメ元でも良いので"Rename... "を実行してみましょう、新しい発見があるかも知れませんよ(反応しなかったときの落胆もかなりのものですが……) 。
リネームの候補がその場に出てくることを「In-placeモード」と呼びます。このオプションを外す人は少ないと思いますが、In-placeモードは「Preferences / Editor」の「Enable in-place mode」でON/OFF可能で、OFFにすると常に「Rename」ダイアログが表示されるようになります。昔話になりますが、元々はダイアログによる指示のみだったのですが、Eclipseのリファクタリングを真似てIn-placeモード取り込みました。その名残として、このオプションが残っています。
図4 「 Preferences / Editor」設定画面
とはいえ、Android Studio本来のリファクタリングの流れは「コマンド実行 → 完了」ではなく「コマンド実行 → ダイアログでオプション設定 → 完了」が正しいのです。リネームも対象によってはIn-placeモードで即実行されず、常にダイアログが出てくるケースがあります。
ダイアログ経由のリファクタリングの例
In-placeモードではなく、ダイアログ経由のリファクタリングについて説明します。リネーム以外のリファクタリングは、だいたいこの流れになります。
あるリファクタリングを実行すると、そのリファクタリング専用のダイアログが表示されます。図5 はリネームのダイアログ例です。ダイアログ下部のボタンより上の部分は、リファクタリングのコマンドやその対象によって微妙に変化します。リネームだけでも、対象がクラス名、引数名、ローカル変数名などによって設定項目が変化します。
図5 「 Rename」ダイアログのバリエーション
※ このオプションをすべて説明するのは骨が折れるので割愛します。要するにリファクタリングの対象範囲を指示しているのです。
この解説での対象は、ダイアログ下部のボタン群です。
図6 リファクタリングダイアログ下部のボタン群
「Cancel」「 Help」は読んで字のごとしなので省略します。「 Refactor」が実行ボタンで、ダイアログに指示した内容でリファクタリングを実行します。解説の本命は残りの「Preview」です。
In-placeモードにしろ、ダイアログの「Refactor」ボタンにしろリファクタリングは即実行で後は仕上げをご覧じろです。ローカル変数やメソッド引数のようにスコープの狭いものならそれでも良いのですが、クラス名の変更など本当にAndroid Studioを信じて即実行していいものかと不安になります。そんなときに使うのが、この「Preview」ボタンです。ボタン名から察するに「リファクタリング後 のプレビューを表示してくれるのか」と期待しがちですが、実は違います。
プレビューを実行してみるとわかりますが、「 Findツールウィンドウ」が表示され、そこにリファクタリングの対象がリストアップされます。つまり「リファクタリング後 」ではなく「リファクタリング前 」がプレビューされるわけです。
図7 「 Preview」ボタンの実行例
この「Findツールウィンドウ / Refactoring Previewタブ」ですが、リファクタリング対象がどこに影響を及ぼすかをカテゴリ別に分類して表示します。これが「プレビュー」の意味になります。
この内容をざっと眺め、問題がなければツールウィンドウ下部にある「Do Refactor」ボタンを押して実行します。
問題というのは「ここはやり過ぎなのでリファクタリングしないで欲しい」箇所があるかどうかです。リファクタリングしてほしくない箇所があったら、そこを選択してコンテキストメニューの"Exclude "を実行します。すると、その箇所に取消線が引かれリファクタリング対象から除外されます。戻す場合は"Include "を実行します(大抵 "Exclude "はDELキー、"Include "はINSキーにショートカットキーが割り当てられています) 。
図8 リファクタリングの対象を指定する
この除外設定("Exclude ")はプレビューの大事な機能なのですが、あまりにも控えめな機能なためか、それほど知られていないように思えます。
リファクタリングのプレビューで表示される「Findツールウィンドウ」は、検索("Find in Path... "や"Replace in Path... ")で表示されるものと似ているようで若干異なります。検索時の「Findツールウィンドウ」については、第18回 を参照してください。ここでは、その差分について説明します。違いはツールバーの右列です。増えている機能について表1 にまとめました。どれもON/OFFのトグルボタンとして機能します。
表1 リファクタリングプレビューの時のツールバーアイコン(増分)
コマンド名 機能
Show Read Access対象が読み込みアクセスを行っている箇所を表示します。
Show Write Access
対象が書き込みアクセスを行っている箇所を表示します。
Show import statements対象がimport
文で使用されている箇所を表示します。
特筆すべき点は「 Preview」です。ツールバーにあるこのアイコンを選択すると「Findツールウィンドウ」の右側に「Previewペイン」が表示されます。ここまでは検索時と同じなのですが、「 Previewペイン」の下部に、それぞれ以下のタブが追加されています(Previewタブ以外はプレビューの対象によって増減します) 。
Previewタブ
Call Hierarchyタブ
Dataflow to Hereタブ
Dataflow from Hereタブ
図9 「 Previewペイン」の表示例
「Previewタブ」は見ての通り対象箇所のコード片が表示されます。「 Call Hierarchyタブ」は、対象箇所の呼び出し階層が表示されます。「 Call Hierarchy」については第18回 を参照してください。最後の「Dataflow to Hereタブ」と「Dataflow from Hereタブ」は本連載ではまだ紹介していない分析機能のひとつです(後の回で紹介します) 。
このようにリファクタリングのプレビューでは、対象と影響箇所を多角的に評価してからリファクタリングを実行するかどうかを判断することができます。ここまでできると、さらに欲が出るのですが、今のところプレビューで分類した除外設定を記録して、他で再利用することはできません。あくまでその時限りです。
おまけ:モジュールのリネーム(非公式)
リファクタリングとはちょっとズレますが、モジュールのリネームについて言及しておきます。
特に凝ったことをしない限り、Android Studioは「1プロジェクト1モジュール」のプロジェクトを作成します。モジュール名は「New Project」ウィザードで指定できますが、ついうっかりデフォルトのままにして「app」にしている事が多いのでは?と思います。
図10 大抵モジュール名は「app」になっている
デフォルトの「app」のままでも困ることはそう無いと思うのですが、そこはそれ「名前重要」なので気になる人にはとても気になると思います。ちなみに、このモジュール名がどこにどう使われるかと言えば、せいぜい生成するAPKファイルの名前 くらいです。
このモジュールのリネームですが、簡単そうに見えて意外と面倒です。まず最初に思いつくのが「Project Structure上でリネームすること」ですが「Project Structure / Modules」ではモジュールの追加・削除ができるだけで、既存のモジュールの名称を変更することはできません。
図11 「 Project Structure / Modules」設定画面
では、どうやるかというと、次のような段取りを踏みます。これは筆者の経験則でやっている手順なので正式なものでは無いことをご了承ください(そもそも、どれほど需要があるのだろう……) 。
1.モジュールをリネーム
「Projectツールウィンドウ」上で対象とするモジュールを選択して、"Rename... "を実行します。「 Select Refactoring」ダイアログが表示されるので、それぞれ順番に「ディレクトリ名のリネーム(Rename dirctory) 」と「モジュール名のリネーム(Rename module) 」の両方を実行します。仮に、モジュール名を「app」から「sample」に変更したとして説明を続けます。
図12 「 Select Refactoring」ダイアログ(クリックすると動きがわかります)
図13 モジュール名の変更後の様子
2.Gradleのビルドスクリプトのつじつまを合わせる
モジュール名が変わった事によるGradle側のつじつま合わせを行います。本来ならモジュールのリネームの時に連動してくれるとAndroid Studioを使っている甲斐があるのですが、まだそこまでやってはくれません(むしろ、たまに余計なことをしたりと不安定です) 。
必ずやるのは <PROJECT_HOME>/settings.gradle
の変更です。これはGradleのマルチプロジェクトに必要な設定ファイルで「どのプロジェクトたちで構成されたマルチプロジェクトなのか」を定義してあります。ややこしくて申し訳ありませんが「Gradleのプロジェクト=Android Studioのモジュール」です。
リスト1 <PROJECT_HOME>/settings.gradleの修正例
include ':app'
↓
include ':sample'
※モジュールのリネームの過程で余計なゴミが混入する場合があるので、なるべく気にかけておいてください。
この他に、サブプロジェクトのbuild.gradle
のdependencies
でリネームしたモジュール(サブプロジェクト)を参照しているものがあれば、それもあわせて修正しておきます。
以上の作業が終わったら、「 Tools → Android」メニューかツールバーから「 "Sync Project with Gradle Files "」を実行してビルドスクリプトの変更をAndroid Studioに取り込んでください。
まとめと次回の予定
リネームを例にAndroid Studioのリファクタリングのやり方を紹介しました。次回は「Refactor」メニューにあるリファクタリング項目をひとつひとつ紹介していきます。