Michael Stum
Michael Stum

Reputation: 180934

Cannot convert X to T even though X should match T?

I'm usually a C# developer, new to Java and I'm trying to setup some code using Generics. My Demo code looks like this:

// Main.java
public static void main(String[] args) {
    MyBase my = getMeStuff();
    System.out.println(my.getSomething());
}

private static <T extends MyBase> T getMeStuff(){
     return new MyDerived(123);
}

// MyBase/MyDerived.java
public class MyBase {
    private final int something;

    protected MyBase(int something) { this.something = something; }

    public int getSomething() { return something; }
}

public class MyDerived extends MyBase {
    public MyDerived(int something) { super(something); }
}

This fails to compile:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
Type mismatch: cannot convert from x.MyDerived to T

at x.Main.getMeStuff(Main.java:14)
at x.Main.main(Main.java:9)

As said, I'm usually a C# Developer, so the code looks good at first glance. I can "resolve" this error by changing getMeStuff to add a cast to (T), but is that cast really necessary? It smells a bit, like I've forgotten something?

Upvotes: 4

Views: 1896

Answers (2)

DerMike
DerMike

Reputation: 16190

Java Generics want to help you achieve type safety.

Let me use a parallel: Let us assume you wrote <T extends MyUIControl> and at one point you use MyButtons with it and somewhere else MyPanels - which cannot be cast safely.

The compiler recognized such a situation in the code.

Upvotes: 1

Chris Hannon
Chris Hannon

Reputation: 4134

The problem is that the return type can be anything that derives from MyBase and you've specifically typed it to MyDerived, which doesn't even work in C# without a cast. The caller could have specified SomeOtherDerived instead, which also derives from MyBase but has nothing to do with MyDerived.

public class MyBase{}
public class MyDerived extends MyBase{}
public class SomeOtherDerived extends MyBase{}
public static <T extends MyBase> T getAnything(){ return (T)new MyDerived(); }

public static void main(String[] args) {
    SomeOtherDerived instance = getAnything(); //This is legal and bad
}

It's also important to note that, even with a cast, this is a very bad thing and can easily result in runtime exceptions when called with any type that's not explicitly provided. This also goes against the concept of generics by nailing down very specific types in what should be a generalized situation.

Upvotes: 5

Related Questions