0

I have the following code that give me trouble compiling.

Give me this error

Error:(20, 22) java: incompatible types: com.company.Main.Impl cannot be converted to T

I only need that interface to work in this function, and I don't want to change the function body much.

Why doesn't this work? and How could I make this work?

    package com.company;

public class Main {

    class Impl implements someinterface, anotherinterface{

        @Override
        public Integer getInteger() {
            return 0;
        }
    }

    class BigObject{
        public Impl get(){
            return new Impl();
        }
    }

    private <T extends someinterface & anotherinterface> Integer myfunc(BigObject bg){

        T xy = bg.get(); // this line will not compile????
        if (xy.isOK()) // from someinterface 
           return xy.getInteger(); // from anotherinterface
        return null;
    }


    public static void main(String[] args) {
    // write your code here
    }
}
user40129
  • 763
  • 1
  • 5
  • 15
  • 1
    Why do you even need the generic parameter in the first place? From the looks of it, `someinterface` (which should be written in [CamelCase](https://en.wikipedia.org/wiki/Camel_case)), contains only the method `getInteger()`. You can replace `T` with `someinterface` – Turing85 Jan 12 '18 at 15:44
  • 5
    `T` is *something* that implements `someinterface`; it's not necessarily an `Impl`. Just use `someinterface xy = ...` instead. – Andy Turner Jan 12 '18 at 15:45
  • 2
    Generics here are nonsensical. Remove the generic declaration and replace `T xy = bg.get();` with `someinterface xy = bg.get();` then debug. – markspace Jan 12 '18 at 15:45
  • @AndyTurner see the updated user case with less trivial example. – user40129 Jan 12 '18 at 15:52
  • @user40129 exactly the same comment. `T` is *something* that implements `someinterface` and also `anotherinterface`; it's not necessarily `Impl`. – Andy Turner Jan 12 '18 at 15:55
  • @AndyTurner, correct, but this function doesn't require it to be impl, just need anything that implement those interfaces. isn't that what generics are for? – user40129 Jan 12 '18 at 15:58
  • 1
    @user40129 Then create a `newinterface extends someinterface, anotherinterface` and replace `T` with `newinterface`. – Turing85 Jan 12 '18 at 15:59
  • Generics are invariant. [Related image](https://i.stack.imgur.com/KjDLw.png). – MC Emperor Jan 12 '18 at 15:59
  • @Turing85, then I have to go to change My Impl implementation to implement newinterface, don't really want to change that code. Thinking this is a giant code base. and you want to be less invasive. – user40129 Jan 12 '18 at 16:01
  • @user40129 ... then replace `T` with `Impl`. `BigObject` does not seem to inherit from anything, so it seems pretty fix to me. Otherwise, you would need to somehow make `BigObject` generic in `get()`'s return type. – Turing85 Jan 12 '18 at 16:09
  • 1
    You can always cast `T xy = (T) bg.get();` if you don't want to change other parts of your code. – Pshemo Jan 12 '18 at 16:11
  • @Pshemo: this is brilliant. however I do want it to give me error if bg.get() doesn't give me the right interface. – user40129 Jan 12 '18 at 16:13
  • 1
    @user40129 If you do the cast, you really do not need any generics. `someinterface si = bg.get(); anotherinterface ai = (anotherinterface) si;`. You know... generics are nothing else than compile-time type assertion, which you totally forego with casts. – Turing85 Jan 12 '18 at 16:13
  • @Turing85: right. I do like the type checking to give me error if what returned doesn't give the correct interface. so I need two temporary objects one for each interface before I can call that. This seems like a lack of feature on java side. – user40129 Jan 12 '18 at 16:17
  • 2
    @user40129 Again the question: why do you need generics? Why not replace `T` with `Impl`? – Turing85 Jan 12 '18 at 16:21

2 Answers2

2

It won't compile because in Java, generics are invariant, see related post.

With the following line of code:

<T extends SomeInterface> Integer myfunc(BigObject bg) { ... }

you are saying that T is something that is some kind of SomeInterface, or, more precisely, a certain type that is a subtype of SomeInterface. The compiler complains about T xy = bg.get(), because bg.get() returns a certain subtype of T, but that type may or may not be the same as Impl.

As an analogy, you are saying something like this:

class Cat extends Animal { }
class AnimalObj {
    public Cat get() {
        return new Cat();
    }
}

private <T extends Animal> Integer myfunc(AnimalObj bg) {
    T xy = bg.get();
    ...
}

T could be a Cat, but it could also be a Dog. Who knows. So that's why the compiler complains.

If you don't care about the subtype, you should drop generics, and write this instead:

private Integer myfunc(AnimalObj bg) {
    Animal xy = bg.get();
    ...
}

Since myFunc accepts a BigObject, which is able to deliver a concrete Impl, you could just replace <T extends someinterface & anotherinterface> by Impl.


Read more

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
  • Yes it can also be a Dog, does it matter. `myfunc` cares only about any type of `Animal`. Right? – tsolakp Jan 12 '18 at 16:15
  • 1
    No, not quite. Not "any type of `Animal`" (then we could just write `Animal xy = ...`) but merely "a specific, but unknown type which is a subclass of `Animal`". They are not the same. – MC Emperor Jan 12 '18 at 16:17
  • Right, but the compiler should complain if bg.get() doesn't have those interfaces, why does the compiler wants to check more than that? – user40129 Jan 12 '18 at 16:32
  • @user40129 According to that logic, `Dog d = new Cat()` would be possible, if they both implement `Strokeable`. The fact that two classes implement the same interfaces, doesn't mean they are interchangeable. – MC Emperor Jan 12 '18 at 16:41
  • @MCEmperor, If , T d canott be a new Cat()? – user40129 Jan 12 '18 at 16:45
  • @user40129 Indeed, because `T` *might* be a `Dog`. Both `Dog` and `Cat` implement `Strokable` and `WillPee`, but they aren't interchangeable. – MC Emperor Jan 12 '18 at 19:17
  • from my function I don't care about Dog cannot be Cat, I just need d.willpee() to return boolean. if I somehow calls d.bark(), d.bark() (and only d.bark()) should cause compilation error – user40129 Jan 12 '18 at 20:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/163079/discussion-between-mc-emperor-and-user40129). – MC Emperor Jan 12 '18 at 22:07
0

Lets change the myfunc a little bit so that it will return the generic type T:

private static <T extends someinterface & anotherinterface> T myfunc(BigObject bg){
    return bg.get();
}

Now you can call it like this:

Impl i1 = myfunc( new BigObject() );

and like this:

Impl2 i2 = myfunc( new BigObject() );

But if BigObject.get would have only returned Impl you would have error with second call that expected Impl2.

tsolakp
  • 5,858
  • 1
  • 22
  • 28
  • this doesn't compile. and gives an error at bg.get() – user40129 Jan 12 '18 at 16:29
  • Yes it does not because `bg.get()` is only returning `Impl`. The example I gave shows that with signature `myfunc` has, `bg.get()` function cannot just return `Impl`. The compiler cannot make sure that the caller of `myfunc` wont specify `Impl2` for T. – tsolakp Jan 12 '18 at 16:34
  • a reasonable compiler will let you compile the function but neither of the function calls. Because it doesn't know if that generic type returned can be translated to Impl/Impl2, but if il is of type (instead of Impl/Impl2), function calls should compile, since there is no ambiguity. – user40129 Jan 12 '18 at 16:59
  • Yes, that is why as others suggested you need to change `bg.get()` to be `public T get()`. This way compiler can be sure that `bg.get()` will handle `Impl` and `Impl2`. – tsolakp Jan 12 '18 at 17:15
  • don't think that's what they suggests, because public T get() on BigObject will not compile. – user40129 Jan 12 '18 at 17:22
  • You also need to cast unfortunately: `return (T)( new Impl() );` – tsolakp Jan 12 '18 at 17:23
  • yeah... seems unavoidable, but compiler has all the information to make a decision, I really shouldn't have to do this. – user40129 Jan 12 '18 at 17:25
  • With cast you telling compiler that: "Trust me I will never call `bg.get()` and expect `Impl2`. If I do I am ok with `ClassCastException`." – tsolakp Jan 12 '18 at 17:26