Skip to content

Repository files navigation

KlassIndex

GitHub Workflow Status (with branch)CodeFactorGitHub LicenseGitHub last commitGitHub Release DateMaven Central

About

KlassIndex is the younger Kotlin brother of atteo/classindex. However, it differs from ClassIndex in various aspects.

AspectsClassIndexKlassIndex
LanguageJavaKotlin
Supported ClassesAny Java classAny Kotlin class
Supported Build ToolsMaven and GradleGradle
Supported ScopesAnnotations, Subclasses, PackagesAnnotations, Subclasses5
Service Loader SupportYesNo, superfluous6
JaxB IndexYesNo
Stores DocumentationYesNo
Android SupportLimited2Yes
Runtime PerformanceGreatEven Greater1
Filtering SupportLimitedComplete functional support3
Extension SupportYesTheoretically
Index external classesYes, by extending the processorYes, using kapt arguments
Jar Shading SupportYesMaybe
Compile Time SafetyLimited4Complete
Class Loader RequiredYesNo
LicenseApache 2.0Apache 2.0

Explanation

  1. ClassIndex stores the qualified names of the classes to load at run time in the jar's resources. The resource parsing and class loading comes at a cost. In contrast, KlassIndex statically compiles the references. That means resource and classloading are not necessary. Excluded, however, are use cases where there are tons unreferenced of classes with only few of them to be loaded. ClassIndex might show a better performance then.
  2. ClassIndex depends on resource and class loading. In use cases in Android where no context is available or a different class loader is to be used, ClassIndex will fail. KlassIndex compiles the references statically to enable full support.
  3. KlassIndex provides all functional methods from Kotlin's Iterable, not just filters.
  4. ClassIndex uses workarounds to detect if classes are valid and still existent. KlassIndex, however, uses statically compiled generated classes so that the compiler can check their validity.
  5. Kotlin does not (yet?) have such a concept such as Java's package files. Thus, package indexing has been removed to be consistent with the Kotlin.
  6. The only use case in which a service loader is to be preferred over statical compilation is in plugin-based systems.

Methods

  • the list of classes annotated by a given annotation getAnnotated()
  • the list of classes implementing a given interface getSubclasses()

Advantages

KlassIndex

  • is faster than reading a file, it is not impacted by the usual performance penalty of the classpath scanning
  • does not depend on a class loader
  • is light-weight and simple
  • supports incremental compilation in IntelliJ and Android Studio

How to use it?

Add Dependency

Gradle

  • Add kapt
plugins {
// Replace with the latest Kotlin version
id "org.jetbrains.kotlin.kapt" version "1.+"
}
  • Add dependencies
// Replace with the latest versions from Jitpack
compile 'fyi.fax.klassindex:library:4.+'
kapt 'fyi.fax.klassindex:processor:4.+'
  • (Optional) Enable kapt build cache
kapt {
useBuildCache =true
}

Gradle in Kotlin DSL

  • Add kapt
plugins {
// Replace with the latest Kotlin version
kotlin("kapt") version "1.+"
}
  • Add dependencies
// Replace with the latest versions from Jitpack
compile("fyi.fax.klassindex:library:4.+")
kapt("fyi.fax.klassindex:processor:4.+")
  • (Optional) Enable kapt build cache
kapt {
useBuildCache =true
}

Annotations

  • Annotate your annotation with @IndexAnnotated
@IndexAnnotated
annotationclassYourAnnotation
  • Retrieve a list of annotated classes at run-time
KlassIndex.getAnnotated(Component::class)

Subclasses

  • Annotate your superclass with @IndexSubclasses
@IndexSubclasses
interfaceYourSuperclass
  • Retrieve a list of annotated classes at run-time
KlassIndex.getSubclasses(YourSuperclass::class)

Index Traversing

Filtering allows you to select only classes with desired characteristics. Here are some basic samples:

  • Selecting only top-level classes
KlassIndex.getAnnotated(SomeAnnotation.class).topLevel()
  • Selecting only classes which are top level and public at the same time
KlassIndex.getAnnotated(SomeAnnotation.class).topLevel().withModifiers(Modifier.PUBLIC)
  • Selecting only the object instances from singleton classes that are annotated with an additional annotation.
KlassIndex.getAnnotated(SomeAnnotation.class).annotatedWith(SecondAnnotation::class).objects()

For more examples, check the test file.

Indexing Without Annotations

Sometimes, you cannot easily use annotations to trigger compile time indexing because you don't control the source code of the classes which should be annotated. For instance, you cannot add @IndexSubclasses meta-annotation to @Exception.

To add the regarding annotations to the desired external class, just add an argument to kapt.

kapt {
arguments {
arg(
"fyi.fax.klassindex.IndexSubclasses", // IndexAnnotated alternatively"java.lang.Exception"// this is a vararg
)
}
}

Please consider that this option only provides you internal classes at the moment. That means, for the Exception example, you will only get your declared exceptions, not all available Java and Kotlin exceptions.

Make sure not to use Kotlin type aliases, they will not be recognized (e.g., not kotlin.Exception).

How the Magic Happens

Annotation Index Processor

KlassIndex indexes your classes at compile time by providing the implementation of the standard annotation processor. The index is then used by kapt utilizing kotlinpoet to generate new Kotlin source files that hold the static references to the indexed classes. The compiler uses the source files as if they were manually written.

Run Time Library

KlassIndex provides a library to access the statically compiled index from the generated classes and to process them.

Why KlassIndex

Speed

Traditional classpath scanning is a veryslow process. Replacing it with compile-time indexing speeds Java applications bootstrap considerably.

Here are the results of the benchmark comparing ClassIndex with various scanning solutions.

LibraryApplication startup time
None - hardcoded list0:00.18
Scannotation0:05.11
Reflections0:05.37
Reflections Maven plugin0:00.52
Corn0:24.60
ClassIndex0:00.18
KlassIndexTBD

Notes: benchmark was performed on Intel i5-2520M CPU @ 2.50GHz, classpath size was set to 121MB.

About

Index Kotlin classes at compile time as if they were statically referenced.

Topics

Resources

Stars

7 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages