5

I have the following interface method definition written in Java:

<T extends View & ISpecificView> T getSpecificView();

A Java-based consumer code is able to operate on this method simply by invoking it and treat the returned value as an object extending the View class and implementing the ISpecificView interface the following way:

getContainer().getSpecificView().whateverTclassMethod()

Trying to invoke the same code in Kotlin I get the Type inference failed: Not enough information to infer parameter T (...) Please specify it explicitly error on the getSpecificView() method. I'd love to provide the type explicitly, but I'm unable to pass any specific class, since it may be any ancestor of the View class that implements the ISpecificView interface. Passing either single View or ISpecificView does not help - it results in the Type argument is not within its bounds. Expected: View! Found ICustomView and vice versa.

Is there any possibility to pass an equivalent to Java's T extends View & ISpecificView in Kotlin while calling a method, so I can make use of it?

mrpasqal
  • 1,000
  • 1
  • 6
  • 26
  • 3
    Btw, this is harder than I thought. At least for my Kotlin knowledge – LppEdd Apr 01 '19 at 21:54
  • 1
    I also thought that converting the Java code to Kotlin would be straight-forward, but in cases when you have a lot abstraction it gets much complicated. – mrpasqal Apr 02 '19 at 07:26

2 Answers2

6

To recap (and I hope I got the question right!), in Java you can do

final View view = getContainer().getSpecificView();  // or
final ISpecificView iview = getContainer().getSpecificView();

In Kotlin the same thing

val view: View = getContainer().getSpecificView()

results in Type inference failed: Not enough information to infer parameter T


After 50 minutes of trying and trying...
Just create a dummy class (abstract?)

abstract class KView : View(), ISpecificView

And use it to explicitly set a generic return type

val view: View = getContainer().getSpecificView<KView>()
view.whateverClassMethod()

val iview: ISpecificView = getContainer().getSpecificView<KView>()
iview.whateverInterfaceMethod()

// or just

(getContainer().getSpecificView<KView>() as View).whateverClassMethod()
(getContainer().getSpecificView<KView>() as ISpecificView).whateverInterfaceMethod()

A specific cast to View or ISpecificView is required, because if you just do

getContainer().getSpecificView<KView>()

you'll get

class your.package$ExtendingClass cannot be cast to class your.package.KView

but that's is perfectly fine.
Even in Java you need to decide if you want a View or an ISpecificView.


This way you'll be able to operate on all the methods of View or ISpecificView.


Really, I don't know what else to try. Hope someone comes up with something better.

Edit: if what you mean is in Java you do

final ExtendedView ev = getContainer().getSpecificView();

well, this is a bit wrong imho, even if it compiles, as you cannot guarantee the return type is really ExtendedView. You just know for sure it extends View and implements ISpecificView.

LppEdd
  • 20,274
  • 11
  • 84
  • 139
  • This looks promising, however a bit hacky than generic solution - providing that I have to deal with several cases like this, I'd need to create a new abstract class for each interface I need to combine with the `View` class. Will check it out. Still it would be nice to simply provide something alike `` for the callers. Maybe this should be a new ticket for Jetbrains? – mrpasqal Apr 02 '19 at 07:24
  • 1
    @mrpasqal Feel free to answer there https://discuss.kotlinlang.org/t/calling-java-from-kotlin-type-inference-for-t-extends-one-two/12191 – LppEdd Apr 02 '19 at 07:40
  • @mrpasqal I updated the answer with more precise code. – LppEdd Apr 02 '19 at 08:03
  • 1
    Good catch. This helps a lot. Since there is no more elegant solution in place, I'm marking this as an accepted answer. Not much elegant, but works. It looks like Kotlin sill has to improve. Thanks for your support @LeppEdd! – mrpasqal Apr 02 '19 at 08:57
0

You should be able to achieve this using the following:

fun <T> getSpecificView() where T : View, T : ISSpecificView {}

Documentation for this can be found here.

seeming.amusing
  • 1,179
  • 1
  • 8
  • 18