skip to Main Content

I’ve been goofing around with Kotlin of recent, and it’s been Awesome!

I was working with Twitter4j library to tryout some stuff with the Twitter API. I wrote this code in Kotlin

object Demo {

    private val twitterStream = TwitterStreamFactory().instance
    @JvmStatic
    fun main(args: Array<String>) {

        val listener = object : StatusListener {

            override fun onStallWarning(warning: StallWarning?) {
                println("Got stall warning:" + warning)
            }

            override fun onScrubGeo(userId: Long, upToStatusId: Long) {
                println("Got scrub_geo event userId:$userId upToStatusId:$upToStatusId")
            }

            override fun onStatus(status: Status) {
                println("@" + status.user.screenName + " - " + status.text)
            }

            override fun onDeletionNotice(statusDeletionNotice: StatusDeletionNotice) {
                println("Got a status deletion notice id:" + statusDeletionNotice.statusId)
            }
            override fun onTrackLimitationNotice(numberOfLimitedStatuses: Int) {
                println("Got track limitation notice:" + numberOfLimitedStatuses)
            }
            override fun onException(ex: Exception) {
                ex.printStackTrace()
            }
        }

        twitterStream.addListener(listener)
        twitterStream.sample()

    }
}

but each time I ran it, I got exception in thread "main" java.lang.IllegalAccessError: tried to access class twitter4j.StreamListener from class co.enoobong.eno.twitter.bot.Demo
at co.enoobong.eno.twitter.bot.Demo.main(Demo.kt:63)

Upon further investigation, Tools->Kotlin->Show Kotlin Bytecode. I decompiled to Java, only to discover that this is what was being generated.

 @JvmStatic
 public static final void main(@NotNull String[] args) {
  Intrinsics.checkParameterIsNotNull(args, "args");
  <undefinedtype> listener = new StatusListener() {
     public void onStallWarning(@Nullable StallWarning warning) {
        String var2 = "Got stall warning:" + warning;
        System.out.println(var2);
     }

     public void onScrubGeo(long userId, long upToStatusId) {
        String var5 = "Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId;
        System.out.println(var5);
     }

     public void onStatus(@NotNull Status status) {
        Intrinsics.checkParameterIsNotNull(status, "status");
        String var2 = "@" + status.getUser().getScreenName() + " - " + status.getText();
        System.out.println(var2);
     }

     public void onDeletionNotice(@NotNull StatusDeletionNotice statusDeletionNotice) {
        Intrinsics.checkParameterIsNotNull(statusDeletionNotice, "statusDeletionNotice");
        String var2 = "Got a status deletion notice id:" + statusDeletionNotice.getStatusId();
        System.out.println(var2);
     }

     public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
        String var2 = "Got track limitation notice:" + numberOfLimitedStatuses;
        System.out.println(var2);
     }

     public void onException(@NotNull Exception ex) {
        Intrinsics.checkParameterIsNotNull(ex, "ex");
        ex.printStackTrace();
     }
  };
  twitterStream.addListener((StreamListener)listener);
  twitterStream.sample();

}

Kotlin was trying to cast listener to StreamListener which is private in the Library (StatusListener extends it) hence the IllegalAccessError

Any ideas how to resolve please?

PS: Here’s a working Java version of the code – https://github.com/yusuke/twitter4j/blob/master/twitter4j-examples/src/main/java/twitter4j/examples/stream/PrintSampleStream.java

PPS: I’m using Kotlin 1.1.4, on IntelliJ IDEA 2017.2.2

2

Answers


  1. Kotlin adds these casts to ensure the correct method is called when dealing with overloaded methods. The problem occurs because a public api addListener expects a package-private interface StreamListener. Trying to create such an api actually causes a warning exactly because of this problem and should be fixed on Twitter4j’s side.

    There is no direct way to fix this in Kotlin, you can work around the issue using a Java helper class and perhaps an extension method:

    class Twitter4jFixer {
        public static void addListener(TwitterStream stream, StatusListener listener) {
            stream.addListener(listener);
        }
    }
    
    
    fun TwitterStream.addListenerFixed(listener: StatusListener) {
        Twitter4jFixer.addListener(this, listener)
    }
    
    Login or Signup to reply.
  2. Kiskae put a good point, Maybe author of twitter4j does not take interactions with other jvm languages into account.

    I don’t think the fix is necessary.

    You can just put your class into package twitter4j if you must use the package private interface twitter4j.StreamListener because of the unwanted casting.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search