2021年4月4日日曜日

4: Gradleにてクラスを定義し使用する

<このシリーズの前の記事 | このシリーズの目次 |

Gradleスクリプト内で定義されたクラスは何になるのか?スクリプトクラス内のインナークラス?'run'メソッド内のローカルインナークラス?その他?

話題


About: Gradle

この記事の目次


開始コンテキスト



ターゲットコンテキスト



  • 読者は、Gradleで定義されたクラスが何になるのか、およびクラスを定義し使用する方法を知る。

オリエンテーション


Gradleでスクリプトを書くための、より良い方法を紹介する記事があります。

Gradleにおける、変数およびプロパティ、いくつかの変数スコープを明らかにする記事があります。


本体

ト書き
Hypothesizer 7が独白する。


1: スクリプト内でクラスを定義する


Hypothesizer 7
スクリプト内でクラスを定義することができる、以下のように。

@Gradle ソースコード
class CustumTaskA extends DefaultTask {
	@TaskAction
	void action () {
		println ("### CustumTaskA working!")
	}
}

自分のクラスを定義する1つの理由は、カスタムタスククラスを定義しなければならないことだ、上記コードにおけるように。

別の理由は、複数のタスクで使用されるユーティリティクラスが欲しいことだ。

そうした必要性を感じたことがない人は幸運であり、定義し始める義務感を感じる必要は特にないが。


2: それは、インナークラスにもローカルインナークラスにもならない


Hypothesizer 7
それはとにかく、そうしたクラスは何になるのだろうか?

スクリプトクラス内のインナークラス、または'run'メソッド内のローカルインナークラスになるのだろうと私は推測したのだが、そうではないということが分かった。

それは、アウタークラスになる。

その事実はどんな結果をもたらすのか?そのアウタークラスは、スクリプトクラスフィールド群、スクリプトプロパティ群、プロジェクトプロパティ群に、直接アクセスできない。

うーん、それは不便だ!スクリプトクラス内にインナークラスを定義できないのか?私が知る限り、できない。


3: スクリプトファイルの外でクラスを定義する


Hypothesizer 7
えーと、スクリプトの中でそうしたアウタークラスを書くことは許されるが、良い方法には思われない、なぜなら、そのクラスは、本当にはそのスクリプトの一部ではないから。

Groovy的には、そうしたクラスは別のGroovyソースファイル('groovy'というファイル名拡張子で)の中に入れることができるが、えーと、GradleはそのようなGroovyソースファイルも'gradle'拡張子ファイルも読まない、私が知る限り。 . . . Gradle は、なぜGroovyをもっと率直に使わないのか、その予期される動作をまずく歪めるのではなく、と私はいぶかる。

えーと、クラスを別のGradleファイルに入れてそのGradleファイルを当該スクリプトへ「apply」することはできるが、1つの問題は、そのクラスはスクリプト内で不可視であることだ、以下がエラー、「unable to resolve class ClassB」を出すとおり。

Classes.gradle

@Gradle ソースコード
class ClassB {
	public static void staticMethodB () {
		println ("### staticMethodB!")
	}
	
	public void instanceMethodB () {
		println ("### instanceMethodB!")
	}
}

build.gradle

@Gradle ソースコード
apply ("from": "Classes.gradle")

ClassB l_classB = new ClassB ()

. . . なぜなのか? . . . その理由は、Gradleファイル毎に別のクラスローダーが使われることだ。

それでは、私はどうすべきなのか? . . . あるクラスインスタンスが、例えば、あるプロジェクトプロパティに入れられていれば、そのクラスインスタンスは、当該スクリプト内でアクセスできる、以下がオーケーであるとおり。

Classes.gradle

@Gradle ソースコード
class ClassB {
	public static void staticMethodB () {
		println ("### staticMethodB!")
	}
	
	public void instanceMethodB () {
		println ("### instanceMethodB!")
	}
}

ext.classB = new ClassB ()

build.gradle

@Gradle ソースコード
classB.instanceMethodB ()

しかし、クラスインスタンスではなく、クラス自体がスクリプト内で使われなければならないとしたらどうする?例えば、カスタムタスククラスは、スクリプト内で使われなければならない、そのクラスからタスクを作成するためには。

実のところ、そのクラスをプロジェクトプロパティに入れることができる、以下がオーケーであるとおり。

Classes.gradle

@Gradle ソースコード
class ClassB {
	public static void staticMethodB () {
		println ("### staticMethodB!")
	}
	
	public void instanceMethodB () {
		println ("### instanceMethodB!")
	}
}

class CustumTaskA extends DefaultTask {
	@TaskAction
	void action () {
		println ("### CustumTaskA working!")
	}
}

ext.ClassB = ClassB.class
ext.CustumTaskA = CustumTaskA.class

build.gradle

@Gradle ソースコード
ClassB.staticMethodB ()

task ("custumTaskA", type: CustumTaskA, {
})

しかし、それでも、そのクラスから'new'オペレーターでインスタンスを生成することができない、スクリプト内では。

私はそういう必要性に突き当たったことがないが、もしそうなれば、ファクトリーメソッドを持てば良いだろう、以下のとおり。

Classes.gradle

@Gradle ソースコード
class ClassB {
	public static void staticMethodB () {
		println ("### staticMethodB!")
	}
	
	public static ClassB createInstance () {
		return new ClassB ()
	}
	
	public void instanceMethodB () {
		println ("### instanceMethodB!")
	}
}

build.gradle

@Gradle ソースコード
def l_classB = ClassB.createInstance ()
l_classB.instanceMethodB ()

「l_classB」の変数タイプを'ClassB'に指定することはできないが、残念ながら!


4: スクリプトオブジェクトまたはプロジェクトオブジェクトをクラスにインジェクトする


Hypothesizer 7
既に言ったとおり、アウタークラスであることの不便さは、そのクラスは、スクリプトフィールド群、スクリプトプロパティ群、プロジェクトプロパティ群にアクセスできない(少なくとも、直接には)ということだ。

もしも、それらの内のいくつかにクラスがアクセスしなければならないのであれば、それらは、クラスインスタンスから可視であるようにされなければならない、何らかの方法で。

私は、通常、プロジェクトオブジェクトをクラスインスタンスへインジェクトする。

そのクラスが、'new'オペレーターで私がインスタンス化するものであれば、私はプロジェクトオブジェクトをコンストラクタ引数として渡すことができる、以下のように。

Classes.gradle

@Gradle ソースコード
class ClassB {
	private Project i_project
	
	public static void staticMethodB () {
		println ("### staticMethodB!")
	}
	
	public static ClassB createInstance (Project a_project) {
		return new ClassB (a_project)
	}
	
	public ClassB (Project a_project) {
		i_project = a_project
	}
	
	public void instanceMethodB () {
		println ("### instanceMethodB!")
	}
}

もしも、クラスがタスククラス('task'メソッドによってインスタンス化される)であれば、私はプロジェクトオブジェクトをコンフィギュレーションクロージャ内でセットすることができる、以下のように。

Classes.gradle

@Gradle ソースコード
class CustumTaskA extends DefaultTask {
	protected Project i_project
	
	@TaskAction
	void action () {
		println ("### CustumTaskA working!")
	}
}

ext.CustumTaskA = CustumTaskA.class

build.gradle

@Gradle ソースコード
task ("custumTaskA", type: CustumTaskA, {
	i_project = project
})


5: プロジェクト拡張部: 私が用いるストラテジー


Hypothesizer 7
私が用いる1つのストラテジーは、プロジェクト拡張部を持つことだ。

. . . それは何だ?えーと、プロジェクト拡張部とは、プロジェクトプロパティで、タスク群で共通に使われるいくつかのフィールドといくつかのメソッドを持つクラスのインスタンスであるもののことだ。

例えば、私は、あるプロジェクト拡張部クラスを定義し、そのインスタンスをプロジェクトプロパティとしてセットする、以下のように。

Classes.gradle

@Gradle ソースコード
@groovy.transform.CompileStatic
class ProjectExtension {
	private Project i_project
	public String i_parameterA
	public String i_parameterB
	
	public ProjectExtension (Project a_project) {
		i_project = a_project
		i_parameterA = "parameter A"
		i_parameterB = "parameter B"
	}
	
	public void methodA () {
		println ("### methoidA!")
	}
	
	public void methodB () {
		println ("### methoidB!")
	}
}

ext.extension = new ProjectExtension (project)

なぜ、私は、そうしたデータやファンクション群を直接にプロジェクトプロパティ群としてセットしないのか、以下のように?

build.gradle

@Gradle ソースコード
ext.i_parameterA = "parameter A"
ext.i_parameterB = "parameter B"
ext.methodA = {
	println ("### methoidA!")
}
ext.methodB = {
	println ("### methoidB!")
}

理由は、そうしたファンクション群はクロージャであり、クロージャは確かに便利であるが、スタティックにコンパイルできず、パフォーマンス上の負担になるからだ。

なぜ、そのクラスインスタンスは、プロジェクトプロパティとしてセットされなければならないのか?

えーと、そうされなければならないということは特にないが、プロジェクトオブジェクトはクラスインスタンスたちにインジェクトされることになっているし、勿論、スクリプト内で可視なので、そうされることによって、当該クラスインスタンスはいたるところで可視になる。


参考資料


<このシリーズの前の記事 | このシリーズの目次 |