3

In Android Studio I have the following error:

java: incompatible types: java.lang.Object cannot be converted to java.lang.String

I think b1 and b2 should be behaving the same, but they are not.

Is there a way to make them behave the same (without changing their type of course)?

Here is the code typed:

public class Test
{
    class A<T>
    {
        T t;
        T getT()
        {
            return t;
        }
    }
    class AS extends A<String>
    {

    }
    class B<T> extends AS
    {

    }

    B<Object> b1;
    B b2;

    public void test()
    {
        String t3 = b1.getT();
        String t4 = b2.getT();
    }
}
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
Dombi Bence
  • 746
  • 7
  • 23
  • Can you copy and paste the code in so I can grab code samples for my answer? – Patrick Murphy Jan 12 '17 at 16:31
  • I've edited my answer, so the code is copyable. :) – Dombi Bence Jan 12 '17 at 16:35
  • Correction, could reproduce with `javac` but not with `eclipsec`. The logic stays the same though; you use erased version of `B`, you use erased version of `A`, so it returns an object as per [JLS](https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.8). This is a dupe of http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it after all. – Jorn Vernee Jan 12 '17 at 16:52

3 Answers3

1

The problem is that B is a parameterized type, but b2 is declared to have raw B as its type.

You demonstrate with b1 that B's type parameter is distinct from A's, even though they have the same name, so that B's type parameter has nothing to do with the return type of the getT() method inherited from A<String>. When you use a raw type, however, you get the full erasure of that type, including of its supertypes.

Since's A's type parameter is unbounded, its erasure produces type Object, and therefore this is the type of b2.getT(). Of course, Object is not assignable to String.

You can resolve this in at least two ways:

  1. Don't use a raw type for b2. If you don't care about its type parameter then use B<?>. Or,

  2. Remove class B's type parameter. It's not used for anything in your example, so that would be the cleanest thing to do. Just because its superclass is generic does not mean that B has to be.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Actually, this wouldn't cause the error that the OP is describing, though this would cause an error after the OP fixes the current error – PMARINA Jan 12 '17 at 16:50
  • I don't follow you, @PMARINA. Do you disagree that in the OP's code, the return type of `b2.getT()` is `Object`? You'll then have a hard time explaining the compiler error. Or do you disagree with my explanation of why that's so? In that case, please share. Be sure you can explain why `b1.getT()` does return `String`, though, when `b1` has type `B`. – John Bollinger Jan 12 '17 at 16:57
  • I disagree with your answer, which states that the current error is being caused by the parameters. The issue is that calling getT() should return an object of type T. This shouldn't be solved by always having the input be of type string. Instead, the OP should add a toString() method and call that, rather than getT(). Your solution to always use String is silly because the OP wouldn't need an extra class for doing things solely with Strings, and wouldn't title it T() unless he/she was planning on using other data types. – PMARINA Jan 12 '17 at 19:13
  • @PMARINA, your opinion is contradicted by the observable facts. The OP's `b1.getT()` returns a `String`, even though `b1` has declared type `B`. You can verify this for yourself via your own compiler. I presume that's why the OP includes it in his example. This is because class `B` inherits the specific realization `A`, not `A` as would be more usual. `B`'s type parameter is therefore *unrelated* to `A`'s, as I indeed explain in my answer. – John Bollinger Jan 12 '17 at 20:47
  • Thank you all for the help! :) I had no idea of this "raw type". :) – Dombi Bence Jan 13 '17 at 09:24
  • @JohnBollinger Why would b1.getT() return a string? It returns an object of type T, which would return a String if and only if it's declared type String `T getT() { return t; }` shoud return a Type T – PMARINA Jan 13 '17 at 16:36
  • @PMARINA, because the relevant `T` is *`A`*'s, which is distinct from `B`'s. `B` inherits `A`, not `A`, so regardless of the particular realization of `B`, its `getT()` always returns `String`. `B` would not otherwise be a correct extension of `A`. But I've now said this about four times. It's ok if you don't understand the explanation, but you can confirm via your compiler that it's true. It is counterproductive for you to assert opinions and make explanations that run contrary to verifiable fact. – John Bollinger Jan 13 '17 at 17:28
0

I think that B without the template object is an incomplete class as opposed to B<Object>. This is why when you are calling it getT() you are actually calling A.getT().

This why in the code below, only String t3 = b2.getT(); fails to compile.

static class A<T>
{
    T t;
    T getT()
    {
        return t;
    }
}

static class AS extends A<String> {}

static class B<T> extends AS {}

static class C extends B<Object> {}

static A a;
static B<Object> b1 = null;
static B b2 = new B();
static C c = new C();

static void test()
{
    Object tt = a.getT();
    String t2 = b1.getT();
    String t3 = b2.getT();
    String t4 = c.getT();
}
Doron Yakovlev Golani
  • 5,188
  • 9
  • 36
  • 60
-1

The issue here is that getT() returns the object. What you need to do is implement a method toString() that gives the value of the T Object in terms of a String (or just change the types for t3 and t4 to T so the declaration comes out to T t3 = b1.getT(); and T t4 = b2.getT();).

In addition, you should do the following instead of the code you have for B.

B<T> b1;
B<T> b2;

Note that you need to initialize B to something before you can call b1.anyMethod() or else you'll get a NullPointer Exception.

PMARINA
  • 304
  • 4
  • 17
  • 1
    I think you've missed the point. The fundamental question here is *why* `getT()` returns `Object` when invoked on `b2` (of type `B`), even though it returns `String` as expected when invoked on `b1` (of type `B`). The suggestion to override `toString()` is a *non-sequitur*; there's no reason to think that it's a string representation of an object of some other type that's wanted. – John Bollinger Jan 12 '17 at 20:54