3

I want use generic interfaces that will be specified by their implementing classes, and all in all this has been working fine, like:

interface Remove <E> { fun remove(entity: E) }

class MyHandler : Remove <MyClass> {
   override fun remove(entity: MyClass) { */do stuff/* }
}

However, I have a case (so far, expecting more to come) where I want the function itself to be generic. Writing the interface is no problem at all:

interface FindByID <E> { fun <K : Serializable> findByID(id: K): E }

I need K to be serializable because that's a requirement of some function I need to call.

The compiler doesn't seem to agree with my implementation attempt. When I do this:

override fun <String> findByID(id: String): User {
    return someFunction(User::class.java, id) as User
}

I get two compiler errors:

  1. overrides nothing
  2. id is not Serializable

However, when I remove override and <String> from the signature, it works fine. This means String is serializable, which my research shows as well.

What seems to be the problem here?

Also, yes, I know that I could work around this issue in a couple of ways, like

  • making the interface function accept Serializable instead of <K : Serializable>
  • not specifying K on override, but on call (myHandler.findByID<String>("myID"))
  • "rerouting" the call, implementing (not overriding) in MyHandler a function that accepts Strings and then internally calls the overriden function

Although open to suggestions, I'm less interested in workarounds, but would rather like to understand and (if possible) solve the actual problem or at least know it can't be done so I can take that into account for planning

1
  • If I'm following, the interface promises that you can call that method with any type K — but your implementation is trying to restrict that to a particular type, which is why it's not allowed. Have you tried moving the type parameter K up to the interface? You could then fix it in the implementing class. Commented Oct 19, 2020 at 13:58

1 Answer 1

5

The current problem

With your current declaration of <String>, you're not specializing the type parameter as you might think. What you're actually doing is declaring a type parameter that happens to be named String (nothing to do with the well-known String type, just an unfortunate name collision). With syntax coloring, you should see that String here is in the color of a type parameter, not the same color as the String type would be. Change this name to anything else, and you'll realize the confusion.

So if we rename this to K, the problems become obvious: problem 1 "overrides nothing" is because your generic type parameter doesn't have the same : Serializable constraint as the findByID method defined in the interface. And problem 2 stems from it.

Solutions

Also, yes, I know that I could work around this issue in a couple of ways, like

  • not specifying K on override, but on call (myHandler.findByID("myID"))

This point is actually the essence of the issue, not a workaround: defining a generic function in your interface actually makes it part of the contract for this function to be generic. Implementations must have a generic type parameter here.

What to do to fix it depends on what you expect to happen.

If you're ok with having generic functions in your implementations, then leave the interface as you declared it, but accept that the implementations have to deal with all possible values of K, which is most likely not what you want here.

If you want to define a specific type in your implementations, K should be part of the interface definition (not the interface method), and the method should not have a type parameter itself, but simply accept the existing K from the interface:

interface FindByID<E, K : Serializable> {
  fun findByID(id: K): E
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! I had contemplated your final suggestion (K being part of the interface definition), but discarded it as another "workaround" because I basically brainfarted, somehow thinking I could later combine overriding and overloading, i.e. having the same function (findByID(id)) overloaded via providing different Types for K. Which I realize is stupid considering each implementation would be ambigous (findbyID(Serializable))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.