0

Note: I have found questions regarding the type casting with the IBinder and Binder but they focus on fixing issues. My issue is understanding why the cast is not illegal in the first place. I have also tried the same operation in BlueJ with replacement classes but using the same structure and it fails at runtime.

Here is the code which contains the cast that I thought should be illegal.

private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName className,
                                   IBinder service) {
        // We've bound to LocalService, cast the IBinder and get LocalService instance
        LocalService.LocalBinder binder = (LocalService.LocalBinder) service; //<------------ this cast right here. Note that LocalBinder extends Binder which implements the IBinder interface
        mService = binder.getService();
        mBound = true;
    }

I thought assigning the IBinder (the service variable) parent class variable to a typed variable of a subclass of the IBinder (i.e. the LocalService.LocalBinder 'binder' variabke) and then down casting IBinder to its subclass was illegal.

In other words I thought, in general syntax that this was illegal: Child = (Child) variableOfTypeParent; //This passes the compiler but gets a class cast exception at run-time.

Though I do understand this to be legal: Child = (Child) variableOfTypeParentAssigned2Child; //But this isn't the case here.

Hopefully the superior minds on here can throw me a bone or show me where I can read up on this kind of casting.

Edit: Here is my code from BlueJ:

interface IBinder {

}

class Binder implements IBinder{

}

class Service{

}

class LocalService extends Service {

IBinder b = new LocalBinder(); 

                        class LocalBinder  extends Binder{

                            LocalService getService(){

                                return LocalService.this; 
                            }
                        }
}

public class BindingTheIsh {

public void tester(IBinder service) {

    **LocalService.LocalBinder binder = (LocalService.LocalBinder) service;**  // <-- this line fails at run-time

}

public static void main(String[] args) {

    IBinder iBinder = new IBinder(){

    }; 

    BindingTheIsh b = new BindingTheIsh(); 

    b.tester(iBinder); 
}
}
Onik
  • 19,396
  • 14
  • 68
  • 91
Micah Simmons
  • 2,078
  • 2
  • 20
  • 38

1 Answers1

0

If it works, then service obviously is an instance of LocalService.LocalBinder. Nothing illegal in casting, it just might fail if you're casting wrong things.

Your example is nothing more than

Object o = "Hi I'm a String!";
myMethod(o);

public void myMethod(Object o) {
    String s = (String)o;
}
Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • Thanks for your answer. But why is it that if I do this: IBinder b = new IBinder() {// implement the interface}; and then try LocalService.LocalBinder binder = (LocalService.LocalBinder) b; I get a class cast exception. The code in the question only works when it's inside that method, when I do it in the way I just outlined it fails. – Micah Simmons Apr 22 '15 at 12:07
  • Because your `b` is not a `LocalService.LocalBinder`! It's an anonymous class, so you can only cast it to the interface it implements (`IBinder`). – Kayaman Apr 22 '15 at 12:14
  • So does that mean that, by making 'b' an anonymous class, it is then no longer a compatible type of LocalService.LocalBinder because LocalService.LocalBinder implemented the interface differently ? Does that also mean that in the original question, because 'service' was not initialised yet and it was just a type declaration (i.e. IBinder service;) , this lack of implementation/ambiguity allowed LocalService.LocalBinder to implement it safely, without causing a cast exception? Thanks for your help! – Micah Simmons Apr 22 '15 at 12:44
  • No, `b` and `service` both implement `IBinder`, meaning they can both be cast to `IBinder` without problems. Since they're different classes, they can't be cast to each other. In the original code it is **known** that the parameter `service` will always be an instance of `LS.LB` and the cast is done. Downcasting like that is however a bad idea and usually indicates that the design is off. Before you continue with Android, I recommend you read some basics of Java starting for example [here](https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html). – Kayaman Apr 22 '15 at 12:51
  • I do recognise that downcasting is a bad idea, but I wasn't the one who wrote the code from in the question, it is taken directly from Android's website. I will re-read the document that you sent me, but I get casting in 90% of the cases, this problem in particular threw me. Though, isn't it the case that 'service' doesn't implement IBinder but rather LS.LB implements it, as service isn't a class - it is the empty interface itself. – Micah Simmons Apr 22 '15 at 13:08
  • `service` is strictly speaking a parameter of type `IBinder`, but in runtime service is a reference to a `LS.LB`. – Kayaman Apr 23 '15 at 06:13
  • yeah thanks. After playing with the code, I understand more now, in as much as like you say, IBinder at runtime is of type LS.LB . And that, when bindService() is called the Android system returns the upcasted member variable of type LS.LB. (which implements IBinder) from the Service class. And it is this upcasted LS.LB object that is downcasted in the onServiceConnected() method. Me not seeing how the original upcasted LS.LB object in the Service class was returned to the onServiceConnected() method by Android is what threw me. Hopefully I'm about 70% right now lol..Thanks for your help! – Micah Simmons Apr 23 '15 at 12:25