Skip to main content
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

// 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

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

FeatureKotlinJava
NullabilityBuilt-in: String?Annotations: @Nullable
Data Classesdata class User(...)Boilerplate or Record (Java 16+)
Propertiesval name: Stringprivate final String name; + getter
Singletonsobject MySingletonEnum with single instance or static
Extension FunctionsFirst-class: fun String.isPalindrome()Not supported
CoroutinesBuilt-in: suspend funVirtual threads (Java 21+) or libraries

Migration from Java Templates

If you’re converting Java templates to Kotlin:
  1. Change the base class: JavaTemplateBaseKotlinTemplateBase
  2. Update dependencies: JavaDependencyKotlinDependency
  3. Adjust syntax: Convert Java syntax to Kotlin in your template output
  4. Handle nullability: Leverage Kotlin’s nullability in GetTypeName()
  5. 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

Build docs developers (and LLMs) love