Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make Kotlin stop casting argument to wrong class(Interface)

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

like image 513
Enoobong Avatar asked Oct 30 '22 03:10

Enoobong


1 Answers

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)
}
like image 115
Kiskae Avatar answered Nov 07 '22 20:11

Kiskae