/KaptInlineClass

Demonstrates an issue caused by kapt generating a private constructor when an inline class is passed as a constructor argument.

Primary LanguageKotlin

KaptInlineClass

Demonstrates an issue caused by kapt generating a private constructor when an inline class is passed as a constructor argument.

This issue is problematic for the Android Architecture Components Room annotation processor, which relies on having a public constructor for classes annotated with @Entity.

Details of unexpected behavior

Inside the project you'll finde Example.kt which has the following contents:

inline class LinkId(val name: String)

@Entity
data class Link(
    @PrimaryKey val id: LinkId
)

@Database(
    version = 1,
    entities = [
        Link::class
    ]
)
abstract class ExampleDatabase : RoomDatabase()

Room requires an object annotated with @Entity to have a public constructor. It appears that Link does, but when the project is built, compilation fails with the following error:

e: <path>\app\build\tmp\kapt3\stubs\debug\me\samthompson\kaptinlineclass\Link.java:7: error: Entities and Pojos must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).
public final class Link {
             ^
e: <path>\app\build\tmp\kapt3\stubs\debug\me\samthompson\kaptinlineclass\Link.java:10: error: Cannot find setter for field.
    private final java.lang.String id = null;

Inspecting the linked stub shows the following:

@androidx.room.Entity()
@kotlin.Metadata(...)
public final class Link {
    @org.jetbrains.annotations.NotNull()
    @androidx.room.PrimaryKey()
    private final java.lang.String id = null;
    
    @org.jetbrains.annotations.NotNull()
    public final java.lang.String getId() {
        return null;
    }
    
    private Link(java.lang.String id) {
        super();
    }
    
    @org.jetbrains.annotations.NotNull()
    public final java.lang.String component1() {
        return null;
    }
    
    @org.jetbrains.annotations.NotNull()
    @java.lang.Override()
    public java.lang.String toString() {
        return null;
    }
    
    @java.lang.Override()
    public int hashCode() {
        return 0;
    }
    
    @java.lang.Override()
    public boolean equals(@org.jetbrains.annotations.Nullable()
    java.lang.Object p0) {
        return false;
    }
}

Note that the stub generated has a private constructor. When id is changed to a String, the stub is generated with a public constructor and compilation succeeds.

Steps to reproduce

Run ./gradlew :app:assembleDebug in the root directory of this project. You'll need the android sdk.

Other details

A private constructor will be generated by kapt even if the value passed into the constructor is not a val/var. E.g.

class Link(id: LinkId)

will generate a stub with a private constructor. Properties that are not part of the constructor and have an inline class do not seem to have any effect on the access modifier of the constructor.