Kotlin cannot access class of Java library, which Java can access

Viewed 876

I am relatively new to Kotlin and recently started to convert some Java projects to Kotlin for practice. I know the Java code is working but I am having trouble getting my Kotlin port running. It seems like Kotlin cannot access Java classes the same way Java can.

I have a class in a Java library which looks like this one:

package foo.bar.utils;

import java.util.List;
import java.util.ArrayList;

public class Foo {

    private List<Bar> bars;

    public Foo() {
        bars = new ArrayList<>();
    }

    static public class Bar {

        private Qux quxs;

        public Qux getQuxs() {
            return quxs;
        }

        public Bar setQuxs(final Qux quxs) {
            this.quxs = quxs;
            return this;
        }

        static class Qux {

            @Override
            public String toString() {
                return "Qux []";
            }
        }

        static public class QuxClass extends Qux {
            private String id;

            public String getId() {
                return id;
            }

            public QuxClass setId(final String id) {
                this.id = id;
                return this;
            }
        }
    }
}

The working Java code looks like this:

package com.example;

import foo.bar.utils.Foo;

public class JavaMain {

    public static void main(String... args) {
        Foo.Bar bar = new Foo.Bar();
        Foo.Bar.QuxClass qux = new Foo.Bar.QuxClass();
        bar.setQuxs(qux);

        System.out.println(bar);
    }
}

The non-working Kotlin code looks like this:

package com.example

import foo.bar.utils.Foo

fun main() {
    val bar = Foo.Bar()
    val qux = Foo.Bar.QuxClass()
    bar.quxs = qux

    println(bar)
}

I omitted code that (I believe) is unnecessary to reproduce this problem.

The Kotlin code does in fact compile but at runtime, it throws Exception in thread "main" java.lang.IllegalAccessError: tried to access class foo.bar.utils.Foo$Bar$Qux from class com.example.MainKt. IntelliJ shows, when hovering over bar.quxs, a hint that Type Foo.Bar.Qux! is inaccessible in this context due to: public/*package*/ open class Qux defined in foo.bar.utils.Foo.Bar but I have troubles understanding that hint.

A possible fix is to change the accessibility of the inner class Qux to public.

I tried OpenJDK 12 and Amazon Corretto 8 with the same result.

I also tried to inspect the decompiled code of the Kotlin and the Java code but could not spot any noteworthy differences.

Calling setQuxs() did not help either.

Is there a way to alter my Kotlin code to get the port running? I really want to understand why Kotlin acts like this.

2 Answers

The problem is easy to see when you check out the JVM bytecode for your kotlin snippet (With a regular bytecode viewer, not the one included with Intellij). Your line bar.quxs = qux becomes:

aload0
aload1
checkcast foo/bar/utils/Foo$Bar$Qux
invokevirtual foo/bar/utils/Foo$Bar.setQuxs(Lfoo/bar/utils/Foo$Bar$Qux;)Lfoo/bar/utils/Foo$Bar;

Or represented as java code:

bar.setQuxs((foo.bar.utils.Foo.Bar.Qux)qux);

The bytecode for your JavaMain class does not include this unnecessary checkcast:

aload1
aload2
invokevirtual foo/bar/utils/Foo$Bar.setQuxs(Lfoo/bar/utils/Foo$Bar$Qux;)Lfoo/bar/utils/Foo$Bar;

Why the kotlin compiler adds this checkcast I can not say. I would lean towards classifying this as a bug or at least unexpected behaviour in the kotlin compiler with no easy fix on your end (Besides modifying the code of the library).

New keyword is not for creating object in Kotlin you add import like this

import foo.bar.utils.Foo.Bar
import foo.bar.utils.Foo.Bar.QuxClass

then create object like

    val bar = Bar()
    val qux = QuxClass()
Related