Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic casting in Kotlin

I have the following classes and interfaces:

public interface ActivityComponent<T extends Activity> {
    void inject(T activity);
}

public interface MyActivityComponent extends ActivityComponent<MyActivity> {
}

public abstract class DaggerActivity extends Activity {
    abstract ActivityComponent getComponent(Context context);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityComponent component = getComponent(this);
    }
}

public class MyActivity extends DaggerActivity {
    @Override
    ActivityComponent getComponent(Context context) {
        MyActivityComponent component = buildComponent();
        return component;
    }

And this analogous (I think?) Kotlin code:

public trait ActivityComponent<T : Activity> {
    public fun inject(activity: T)
}

public abstract class DaggerActivity : Activity {
    protected abstract fun getComponent(context: Context): ActivityComponent<Activity> // Type required

    override fun onCreate(savedInstanceState: Bundle?) {
        var component = getComponent(this)
    }
}

public class MyActivity extends DaggerActivity {
    override fun getComponent(context: Context): ActivityComponent<Activity> {
        val component: MyActivityComponent = buildComponent()
        return component as ActivityComponent<Activity>
    }
}

Note: MyActivityComponent implementation is always in Java so that Dagger can process it.

The "problem" is that MyActivity.getComponent() in Kotlin requires a cast to the return type even though MyActivityComponent subclasses ActivityComponent.

My understanding of Kotlin's generics is admittedly weak, and I'm having trouble translating from Java's generics. Can someone explain why this cast is necessary or, preferably, the correct implementation which removes the need for casting?

I've also tried things like:

protected abstract fun <E : Activity> getComponent(context: Context): ActivityComponent<E>

and

protected abstract fun <A: Activity, E : ActivityComponent<A> getComponent(context: Context): E

With the same result (casting required).

like image 258
Michael Pardo Avatar asked Feb 18 '15 18:02

Michael Pardo


People also ask

What is casting in Kotlin?

Kotlin Android. Type check is a way of checking the type( DataType ) or Class of a particular instance or variable while runtime to separate the flow for different objects. In few languages, it's also denoted as Run Time Type Identification (RTTI) .

What is generic function in Kotlin?

Generics means we use a class or an implementation in a very generic manner. For example, the interface List allows us for code reuse. We are able to create a list of Strings, of integer values and we will have the same operations even if we have different types.

How do you cast objects in Kotlin?

To cast Kotlin objects from one type to another, you can use the is and as operator. The is operator is used to cast types implicitly while the as operator is used to cast explicitly.

Does Kotlin have type erasure?

Type Erasure. As with Java, Kotlin's generics are erased at runtime. That is, an instance of a generic class doesn't preserve its type parameters at runtime. For example, if we create a Set<String> and put a few strings into it, at runtime we're only able to see it as a Set.


1 Answers

The difference is that in Java you are using a raw type ActivityComponent as a return type for getComponent(). Raw types are Java's legacy mechanism introduced in Java 5, mainly for backward compatibility with Java 4 collections.

Kotlin does not have raw types. Instead, you can use a "star-projection", i.e. ActivityComponent<*>, which is vaguely analogous to Java's ActivityComponent<?>, which I would recommend to use instead of a raw type anyways.

So, the solution would be:

fun getComponent(context: Context): ActivityComponent<*>
like image 153
Andrey Breslav Avatar answered Sep 27 '22 19:09

Andrey Breslav