The Intent.Modules.Common.Kotlin module provides support for generating Kotlin code, leveraging Kotlin’s modern features while maintaining compatibility with the JVM ecosystem.
Kotlin support in Intent Architect is currently maintained in an archived state. While functional, it may not have all the features available in the C#, Java, or TypeScript modules. Consider this when planning your code generation strategy.
Template Base Class
All Kotlin templates inherit from KotlinTemplateBase<TModel>, which provides:
Automatic import management : Manages Kotlin imports
Package resolution : Resolves fully qualified type names based on package structure
Dependency management : Register Maven/Gradle dependencies
Type resolution : Converts metadata types to Kotlin types with nullability support
Basic Template Structure
public class MyKotlinTemplate : KotlinTemplateBase < ClassModel >
{
public const string TemplateId = "MyCompany.MyKotlinTemplate" ;
public MyKotlinTemplate ( IOutputTarget outputTarget , ClassModel model )
: base ( TemplateId , outputTarget , model )
{
// Configure type sources
AddTypeSource ( "OtherKotlinTemplate.TemplateId" );
}
public override ITemplateFileConfig GetTemplateFileConfig ()
{
return new KotlinFileConfig (
className : Model . Name ,
package : $" { OutputTarget . Name } .domain" ,
relativeLocation : "domain" );
}
public override string TransformText ()
{
return $@"
package { Package }
import java.time.Instant
class { ClassName } {{
// Generated code
}}" ;
}
}
Key Properties and Methods
Package & ClassName
Import Management
Dependencies
Type Resolution with Nullability
// Access the configured package and class name
public string Package { get ; }
public string ClassName { get ; }
public string Namespace { get ; } // Alias for Package
Kotlin Language Features
While Intent Architect’s Kotlin support currently uses text-based templates (T4 templates) rather than a fluent builder API, you can still generate modern Kotlin code:
Data Classes
public override string TransformText ()
{
return $@"
package { Package }
data class { ClassName } (
{ string . Join ( ", \n " , Model . Properties . Select ( p => $" val { p . Name . ToCamelCase ()} : { GetTypeName ( p . TypeReference )} " ))}
)" ;
}
Generated Output:
package com.company.domain
data class User (
val id: String ,
val name: String ,
val email: String ?
)
Sealed Classes and When Expressions
public override string TransformText ()
{
return $@"
package { Package }
sealed class Result<out T> {{
data class Success<out T>(val value: T) : Result<T>()
data class Error(val message: String, val cause: Throwable? = null) : Result<Nothing>()
object Loading : Result<Nothing>()
}}
fun <T> handleResult(result: Result<T>) {{
when (result) {{
is Result.Success -> println( "" Success: ${{result.value}} "" )
is Result.Error -> println( "" Error: ${{result.message}} "" )
Result.Loading -> println( "" Loading... "" )
}}
}}" ;
}
Extension Functions
public override string TransformText ()
{
var extensions = Model . Operations . Select ( op => $@"
fun { Model . Name } . { op . Name . ToCamelCase ()} ( { string . Join ( ", " , op . Parameters . Select ( p => $" { p . Name . ToCamelCase ()} : { GetTypeName ( p . TypeReference )} " ))} ): { GetTypeName ( op . ReturnType )} {{
// Implementation
throw NotImplementedError( "" Not yet implemented "" )
}}" ). ToList ();
return $@"
package { Package } .extensions
import { Package } . { Model . Name }
{ string . Join ( " \n " , extensions )} " ;
}
Coroutines and Suspend Functions
public override string TransformText ()
{
AddImport ( "kotlinx.coroutines.*" );
AddImport ( "kotlin.coroutines.CoroutineContext" );
return $@"
package { Package }
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
class { ClassName } (private val dispatcher: CoroutineContext = Dispatchers.IO) {{
suspend fun fetchData(id: String): Result<Data> = withContext(dispatcher) {{
try {{
val data = repository.getData(id)
Result.Success(data)
}} catch (e: Exception) {{
Result.Error( "" Failed to fetch data "" , e)
}}
}}
fun fetchDataAsync(id: String): Deferred<Result<Data>> =
CoroutineScope(dispatcher).async {{
fetchData(id)
}}
}}" ;
}
Kotlin-Specific Type Handling
Nullability
Kotlin’s null safety is a core feature. The template base handles this automatically:
// Type reference with IsNullable = true
var nullableString = GetTypeName ( stringTypeReference );
// Returns: "String?"
// Type reference with IsNullable = false
var nonNullString = GetTypeName ( stringTypeReference );
// Returns: "String"
// Collection with nullable elements
var nullableList = GetTypeName ( listTypeReference , "List<{0}>" );
// Returns: "List<String>?" or "List<String?>?" depending on configuration
Collections
Kotlin has distinct mutable and immutable collections:
// Immutable collections (default)
var list = "List<String>" ;
var set = "Set<User>" ;
var map = "Map<String, User>" ;
// Mutable collections
var mutableList = "MutableList<String>" ;
var mutableSet = "MutableSet<User>" ;
var mutableMap = "MutableMap<String, User>" ;
// In your template
var collectionType = Model . IsReadOnly
? "List<{0}>"
: "MutableList<{0}>" ;
AddTypeSource ( otherTemplate , collectionType );
Android Development
When generating Kotlin for Android:
public override string TransformText ()
{
AddImport ( "android.os.Bundle" );
AddImport ( "androidx.appcompat.app.AppCompatActivity" );
AddImport ( "androidx.lifecycle.ViewModelProvider" );
return $@"
package { Package }
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
class { ClassName } Activity : AppCompatActivity() {{
private lateinit var viewModel: { ClassName } ViewModel
override fun onCreate(savedInstanceState: Bundle?) {{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ { Model . Name . ToSnakeCase ()} )
viewModel = ViewModelProvider(this)[ { ClassName } ViewModel::class.java]
setupObservers()
}}
private fun setupObservers() {{
viewModel.data.observe(this) {{ data ->
// Update UI
}}
}}
}}" ;
}
Maven/Gradle Dependencies
Kotlin projects typically use Gradle. Register dependencies that will be added to build.gradle.kts:
// In your template constructor
AddDependency ( new KotlinDependency (
groupId : "org.jetbrains.kotlinx" ,
artifactId : "kotlinx-coroutines-android" ,
version : "1.7.0" ,
scope : "implementation" ));
AddDependency ( new KotlinDependency (
groupId : "io.ktor" ,
artifactId : "ktor-client-core" ,
version : "2.3.0" ,
scope : "implementation" ));
AddDependency ( new KotlinDependency (
groupId : "org.jetbrains.kotlin" ,
artifactId : "kotlin-test" ,
version : "1.9.0" ,
scope : "testImplementation" ));
Interoperability with Java
Since Kotlin runs on the JVM, you can reference Java types:
// Import Java types
AddImport ( "java.time.Instant" );
AddImport ( "java.util.UUID" );
// Use @JvmStatic for Java interop
public override string TransformText ()
{
return $@"
package { Package }
import java.util.UUID
class { ClassName } {{
companion object {{
@JvmStatic
fun create(): { ClassName } {{
return { ClassName } (id = UUID.randomUUID().toString())
}}
}}
}}" ;
}
Best Practices
Embrace Kotlin's Features
Take advantage of Kotlin’s language features:
Data classes for DTOs
Sealed classes for type-safe state management
Extension functions instead of utility classes
Default parameters instead of overloads
Named arguments for clarity
Kotlin’s null safety is one of its best features. Generate non-nullable types by default, only using nullable types (Type?) when truly needed.
Use val over var, immutable collections over mutable ones, and data classes with immutable properties.
Kotlin’s type inference is powerful. You can often omit type declarations: val name = "John" // Type inferred as String
val users = listOf (user1, user2) // Type inferred as List<User>
Comparison with Java
Feature Kotlin Java Nullability Built-in: String? Annotations: @Nullable Data Classes data class User(...)Boilerplate or Record (Java 16+) Properties val name: Stringprivate final String name; + getterSingletons object MySingletonEnum with single instance or static Extension Functions First-class: fun String.isPalindrome() Not supported Coroutines Built-in: suspend fun Virtual threads (Java 21+) or libraries
Migration from Java Templates
If you’re converting Java templates to Kotlin:
Change the base class : JavaTemplateBase → KotlinTemplateBase
Update dependencies : JavaDependency → KotlinDependency
Adjust syntax : Convert Java syntax to Kotlin in your template output
Handle nullability : Leverage Kotlin’s nullability in GetTypeName()
Update file extensions : .java → .kt
Limitations
The Kotlin module is in an archived state and lacks:
A fluent builder API (like CSharpFile or JavaFile)
Advanced code merging capabilities
Some newer Kotlin language features
For complex Kotlin code generation, you may need to rely more on string templates and manual formatting.
See Also
Java Support Kotlin’s sibling language on the JVM
C# Support Similar module architecture and patterns
Template Basics Learn about Intent templates
Type Resolution Understanding cross-template type references