これらのファイルには、コンパイラーが自動的に生成したクラスのコードが含まれています。 上記のプログラムのテキストでは、コメントに、生成されたクラスの名前がプログラム内の宣言の場所に従って示されています。
上記の表は、生成されたクラスファイルが、作成対象のコードブロックで実行されるコードに比例して非常に大きいことを示しています。 この例では実際に有用なコードのボリュームが事実上ゼロであるという事実にもかかわらず、作成されたクラスのボリュームはプログラム「 KMainKt.class
」のメインファイルのサイズのほぼ4倍です。 これは、多くの追加情報がクラスファイルに格納されるためです。クラスコードの量が少ない場合、クラスファイル内の有用な情報の量はその一部になります。
自動クラスが実行するコードが大きい場合、結果のファイルのボリュームは無視できます。 ほとんどの場合、自動クラスのコードが非常に小さい場合、プログラムで説明されている匿名ブロックごとに1〜1.5Kbのファイルの作成を有効または許容範囲で呼び出すことはかなり困難です。
自動生成されたクラスは、アセンブルされた形式のプログラムのサイズに直接影響します。 プログラムで匿名コードを積極的に使用すると、そのサイズは急速に増大する可能性があります。 多くの場合、プログラムのサイズは重要な役割を果たします。 これは、モバイルアプリケーションまたはWEBアプリケーション、およびパーソナルコンピューターで実行するように設計されたアプリケーションの両方に適用できます。
この例でコンパイラがクラスを自動的に作成するすべての場所を考慮すると、クラスは一意のコードを持つブロックに対してのみ作成されるという事実に関連する最適化があり、他のすべての場合は既存のクラスのオブジェクトが使用されると想定できます。
「余分な」サイズに対処する方法は?
Kotlin
プログラムでは、匿名コードの使用が非常に広く普及しており、間違いなくその記述と使用の容易さがこの言語の基本的な利点の1つです。 匿名コードのアプリケーションが豊富にあるため、各コードのクラスの生成によりプログラムのボリュームが増加するという問題は、非常に大きな問題になる可能性があります。
コンパイルされたアプリケーションでファイルに冗長情報を保存するには、2つの方法があります。
1つは、 JVM
用にコンパイルされたファイルからサービス情報を削除するように設計されたさまざまなツールを使用することです。
このグループの非常に興味深いツールの1つがProGuard
です。 これは、収集したJAR
アーカイブをアプリケーションとともに再パッケージ化し、そこからすべてのサービス情報を削除できるフリーウェアツールです。 単にサービス情報を削除するだけでなく、クラスコードを最適化するという素晴らしい仕事をします。 ボーナスとして、このツールは「 Obfuscator
ツール」として使用できます。 コードからアプリケーションロジックを回復することを困難にするツール。
これは強力なツールですが、そのレビューはこの記事の範囲外です。
2番目の方法(最初の方法と組み合わせることができます)は、 Kotlin
コンパイラの機能を使用することです。 この機能については、次のセクションで説明します。
内部自動クラス
2つの方法の説明を変更して、この例を試してみましょう。
inline fun <T> cAction(v : T, cb : T.() -> Int) = println(v.cb()) inline fun Action(cb : () -> Int) = println(cb())
この場合、関数の記述にinline
を追加しました。
プログラムをコンパイルした後、自動クラス用にコンパイラーによって作成されたファイルのリストは、「 KMainKt$Test$1.class
」、「 KMainKt$Test$2.class
」、「 KMainKt$Test$cb$1.class
」の3つに削減されました。 つまり 「Action」および「cAction」関数にパラメーターとして渡されないクラスのみがあります。 同時に、 KMainKt.class
ファイルのサイズは約200バイト増加しました。
残りのクラスはどこに行きましたか?
inline
修飾子で宣言されたすべての関数のコードは、関数呼び出しの場所に直接挿入されました。 これに加えて、コンパイラーは同じ名前の別々の関数を作成しました。 言語の構文は、宣言された関数の使用を許可しません。 呼び出そうとすると、そのコードが使用場所に配置されますが、静的コピーが作成され、 reflection
介してこの関数にアクセスできるようになります。
どのように機能しますか?
Kotlinの匿名コードのブロックは、使用場所に応じて、自動オブジェクトの助けを借りて実装するか、呼び出しサイトに直接配置できます。 最初のケースでは、特定のクラスのオブジェクトが作成され、その説明はこのセクションにあります。2番目のケースでは、オブジェクトなどのブロックはまったく存在しません。
自動クラスを作成する場合、Kotlinは2種類のオブジェクトを作成できます。
- あるクラスのオブジェクト、カスタムクラスの子孫。
- コードブロックを呼び出すための特別なクラスを表すオブジェクト。
カスタムタイプオブジェクト
最初のメソッドは、匿名クラス、ラムダ関数を作成し、デリゲートを使用してインターフェイスを実装するために使用されます。 この場合、指定された(明示的または暗黙的に)ユーザークラスの後継であるクラスが作成され、そのようなクラスのメソッドの1つにコードブロックが配置されます。
コード fun add(i : ActionListener) {} add(object : ActionListener { override fun actionPerformed(e : ActionEvent?) {} }) add(ActionListener { e -> })
匿名クラス用に自動クラスが作成された場合、そのクラスのオブジェクトは宣言の場所で構築され、パラメーターとして宛先に渡されます。 機能的インターフェースを実装するためにクラスが作成され、ローカル変数をキャプチャしない場合、このクラスの以前に作成されたシングルトンが使用されます。 ローカル変数がキャプチャされると、新しいオブジェクトが構築され、キャプチャされたすべての変数へのリンクとして、パラメータとしてコンストラクタに渡されます。
そのようなクラスを呼び出す実装とメソッドは次のようになります // : add(ActionListener { e -> }) class AutoClassForCase1 : ActionListener { override fun actionPerformed(e : ActionEvent?) { // - } companion object { @JvmField val INSTANCE = AutoClassForCase1() } } add( AutoClassForCase1.INSTANCE ) //
ローカル変数のキャプチャ
Kotlin
では、 Java
とは異なり、匿名コードで任意のタイプのローカル変数をキャプチャできます( Java
「 effectively final
」である必要があります)。したがって、キャプチャされた変数の自動クラスへの転送は特別なクラス「 Ref<T>
」を通じて行われます。 このアプローチの結果、ローカル変数が少なくとも1つのコードブロックによってキャプチャされた場合、使用する場所でリンクを自動的にラップし、このリンクを使用してすべての呼び出しが行われます。 このメソッドは、 Java
で使用されるメソッドと比較して追加のオーバーヘッドをもたらしますが、変数の値を変更できます。
ローカル変数キャプチャコードブロックがローカル変数をキャプチャすると、コードは次のようになります。
fun Test() { val local = 10 Action{ local+1 } local + 2 }
これになります:
fun Test() { val local = Ref<Int>(10) // Action{ local.value + 1 } // local.value + 2 // local.value = null // }
この場合、次の点に注意する必要があります。
- コードブロックからキャプチャされた変数の値は、関数本体のリンクと同じオブジェクトを含む自動クラスのオブジェクトを介してアクセスされます。 このオブジェクトは、コードブロックの宣言の場所で作成され、自動クラスオブジェクトのコンストラクターに渡されます。
- コードが現在のコンテキストから変数をキャプチャする場合、シングルトンが自動クラスのオブジェクトとして使用されるのではなく、新しいオブジェクトが作成されます。 これは、コンストラクターを呼び出して、キャプチャされた変数への参照を渡すために発生します。
- 関数の最後に、Kotlinはキャプチャされたすべての変数のオブジェクトクリーンアップコードを自動的に生成するため、関数の終了後、ローカルオブジェクトへのリンクの数は、作成された自動オブジェクトの数と常に等しくなります。
コードブロックのオブジェクト
匿名コードブロックを呼び出すための上記の例は、コンパイラによっておよそ次のように実装されます。
匿名コードブロックの実装 // class AutoClassForMethod : Function0<Int> { // private val local : Ref<Int> // constructor( l:Ref<Int> ) { local.value = l.value } // fun invoke() : Int { // } } // Action( AutoClassForMethod(local) )
この例では、簡単にするために、 Any
( Kotlin
コードのこの部分はJava
で記述されているため実際にはObject
)から特定の型への中間パラメーター変換のレイヤーを省略しました。 これは理解に影響しません。
すべてのKotlinコードブロックは、常にFunctionX<Ret[,Param1[,...]]>
タイプFunctionX<Ret[,Param1[,...]]>
は、任意の数のパラメーターを使用してメソッド呼び出しを実装するテンプレートクラスのセットです。 テンプレートの最後のタイプは戻り値のタイプ、最初のタイプは最初のパラメーターのタイプなどです。 クラス名に
を使用して、メソッドパラメーターの数を示しました。 つまり Kotlinで()->Unit
として説明されているメソッドは、実際の型Function0<Unit>
を持ち、 (Int,String)->Double
型のFunction2<Int,String,Double>
などとして説明されています。
FunctionX
テンプレートは、 Kotlin
ライブラリ " kotlin-runtime.jar
"に記述されており、プログラムで使用できます。 ()->Unit
形式で「標準」構文を使用し、 Function0<Unit>
テンプレートを使用してコードブロックを記述する構文は、まったく同じです。 これを確認するには、元の例の関数宣言を置き換えます。
fun <T> cAction(v : T, cb : Function1<T,Int>) = println(cb.invoke(v)) fun Action(cb : Function0<Int>) = println(cb.invoke())
プログラムは以前のようにアセンブルされ、実行されます。
以前に使用された構文「 T.() -> Int
」は、テンプレートの使用に完全に透過的に置き換えられ、メソッドが呼び出されるオブジェクトのタイプに対応する追加のタイプを渡すことに注意してください。 つまり 静的関数の呼び出しとオブジェクト関数の呼び出しの違いは、追加パラメーター「 this
」のみです。
これで何が便利ですか?
単純なユーザーの観点からは、利用可能な言語の構文はここではあまり役に立ちません。
ただし、ライブラリの開発者、または動的メソッドリストを使用して複雑な制御アルゴリズムを実装する人の観点からは、自動オブジェクトの配置方法とコードブロックの処理方法を知ることは非常に役立ちます。
— .
inline-
inline
. .
inline
, . つまり " ++
", . inline- Kotlin
- inline- " ++
". , .
inline
, , . , . つまり " inline
" .
inline- inline-
, " inline
" , . , " inline
", ..
: inline- class Holder { var cb : (()->Unit)? = null inline fun setup( cb:()->Unit ) { this.cb = cb
inline
- inline
. , , - . , , inline-, , " noinline
".
noinline class Holder { var cb : (()->Unit)? = null inline fun setup( cb:()->Unit, noinline delayed:(()->Unit)?=null ) { this.cb = delayed cb() CallCodeWithDelay() } fun DelayPassed() = cb?.invoke() }
++
inline
. Kotlin
. , , . , . " inline
" .
, inline
, . , , Kotlin
, Lint
, , inline
. , , .
@Suppress("NOTHING_TO_INLINE") inline fun SetupControl( ctl: Label ) { ctl.text = "Label" ctl.setSize( 100,100 ) } fun Test() { SetupControl( Label() ) }
SetupControl
"" , .
" ++
", , , inline
.
, Kotlin
return
. , inline
, .
fun Test1() : Int { Action { return 3 } }
inline
, , return
, , . つまり , " Test
". return
— .
, inline
, , .
JVM
, , . , , ..
: fun <T,P> templateTest(value : T) : P { if ( value is P )
inline- " reified
".
inline fun <T,reified P> templateTest(value : T) : P { if ( value is P )
. , templateTest , . , Kotlin reified
,
, , , refrection
, , reified
, .
This function has a reified type parameter and thus can only be inlined at compilation time, not called directly
inline-
, inline
, .
Kotlin
. . .
: ,
inline-.. , .
, , . , , , «Kotlin», «Ref». , . , . inline- , ..
, , .
- inline- .
inline-, , "".
: , , ..
, . , .
, .
- JVM Java 7 Java 8 65534 (.. 65536 , ). inline- , . , .
, inline- . Kotlin
, — .
, .
inline-, :
, , , , . -, .
, (.. -inline- ). inline- , , , , , . つまり , inline , — ..
. , .
- , . — . .