6

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.

1
  • 1
    I can't see anything obviously wrong with your Kotlin code; that's exactly how I'd translate the Java. If no-one here comes up with an explanation, I'd see if it's mentioned on the Kotlin YouTrack bug-reporting site (and maybe consider raising an issue there if not). Commented Jul 27, 2020 at 9:47

2 Answers 2

2

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).

Sign up to request clarification or add additional context in comments.

Comments

0

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()

4 Comments

Why mention new, when the question doesn't include that in its Kotlin code? (Only in its Java code, where that's needed.) And why wouldn't qualified names be just as valid as imported ones?
But you are using Java class in Kotlin function. So even if class is in java the way it is going to use in Kotlin code is Kotlin way
Both Java and Kotlin/JVM classes are compiled to Java bytecode; once compiled, a class is a class, and there's little or no practical difference between them. (That interoperability is one of the selling points of Kotlin: Java classes can use Kotlin classes, and vice versa, without knowing which language they were compiled from.)
Yes, there are Kotlin language rule before converting class file. So by those rule this is a way for your question. Accept as answer....

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.