5

Possible Duplicate:
Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?
Java Generics — Assigning a list of subclass to a list of superclass

With raw types you can easily say something like this.

[...] // MyClass is generic with upper bound of Object

MyClass c = new MyClass<Double>();

[...]

But this isn't allowed

MyClass<Number> c = new MyClass<Double>() 

I don't understand why this is. My book tells me why the second doesn't work, because you can't add a Integer to a MyClass<Double>. What it doesn't explains is why MyClass<Double> is a subclass of MyClass<Object> (or the equivalent raw type form), since double is subclass of object.

So why is the first allowed if the second form isn't. Please realize I am new to this.

Edit: Taking it a step further what would happen in the first example if Number was the upper bound?

You can see here the effects of type erasure

class Untitled {
public static void main(String[] args) {

}
public static<T> void c(T t)
{
    t.SubClassMethod();// this won't work because class Object has no such method. But if I change the upperbound to SubClass it will.
            t.toString() // this will because class Object has such a method
}
}

My point is that why should it matter what the Generic is declared as if it ultimately becomes treated as the upper bound anyway?

Community
  • 1
  • 1
rubixibuc
  • 7,111
  • 18
  • 59
  • 98
  • My question is the opposite, I'm asking why can you assign to a raw type from a subclass – rubixibuc Mar 18 '12 at 23:47
  • The premise is wrong for that question as well, because of type erasure it's only concern is whether the upper bound works with the method, and uses the type to check for casting. At least this is what I've read – rubixibuc Mar 18 '12 at 23:49

4 Answers4

4

Try this:

MyClass<? extends Number> c = new MyClass<Double>();
Bohemian
  • 412,405
  • 93
  • 575
  • 722
3

Suppose that MyClass is like this:

public class MyClass<T>{
  T value;

  public void foo(T arg){
    value = arg;
  }
}

and then those two other classes:

class A{ }

class B extends A { }

now imagine what happens if you do:

MyClass<A> container = new MyClass<B>();
container.foo(new A());

you'd try to put an A into a field of type B. The restriction you are facing is thought to prevent such things. C# has a nifty solution in terms of in and out parameters to generics...

miniBill
  • 1,743
  • 17
  • 41
  • 1
    `new T[2]`?! Uh oh, compiler error! – Jeffrey Mar 18 '12 at 23:45
  • 1
    @Jeffrey If you assume that one part /does/ compile, this answer illustrates the problem very succintly by analogy. – millimoose Mar 18 '12 at 23:48
  • Yes, that part I get, what I don't understand is why the first one compiles without error – rubixibuc Mar 18 '12 at 23:51
  • With type erasure that function treats everything as an object anyway and then casts if it must – rubixibuc Mar 18 '12 at 23:52
  • But your assuming that array is of type double, in this case it is of type Object when it compiles. All method calls are tested based on the upper bound. So if it works for the default of Object in this case, it will work for anything that is a subclass of Object – rubixibuc Mar 18 '12 at 23:59
  • Ok, let me edit it to be more clear – miniBill Mar 19 '12 at 00:03
  • Done, now it should be more clear – miniBill Mar 19 '12 at 00:06
  • Yes but when it compiles T becomes class Object. The only point of using generics is to makes sure you instances match up. When the compile runs that function it will make sure that it will work and run for every object. Even so I do understand the logic of it. It provides type safety even though the reference is of type object. What I don't understand is why you can assign anything to a raw type then? – rubixibuc Mar 19 '12 at 00:11
  • Because a raw type is basically a Class, so in THAT case there are no problems, because you can safely assume that every T is an object – miniBill Mar 19 '12 at 00:13
  • Right it wouldn't let you do anything in the function foo that it couldn't perform on a object of class Object. It's polymorphic. The issues is when you perform those functions they will compute based on their own implementation. – rubixibuc Mar 19 '12 at 00:16
3

The first thing to eliminate your confusion is to understand that the Raw Type is not part of the generics system at all. It is in no way equivalent to Something<Object>. Basically, the reason the Raw Type exists at all is just for backward compatibility. When Generics were introduced in Java 5, some existing classes and interfaces were retroactively made generic. In order for old code that didn't have generic declarations to compile in the Java 5 or later compiler, the Raw Declarations are legal.

Any comparison between the way the Raw Type behaves and the way the Parameterized type behaves is in a way a bit fundamentally spurious. The intent of the design is not that the Raw Type be considered an 'alternative' to declaring a parameter. New code that uses the Raw Type is incorrect code. It's legal to compile it so that old code still works.

My point is that why should it matter what the Generic is declared as if it ultimately becomes treated as the upper bound anyway?

Because the entire point of the generics is to prevent ClassCastException. It will be treated as the actual declared type when someone, for example, someone takes the object back out of the list and assigns it to the specific type they're expecting. The compiler is making a promise that it guarantees this will succeed, so it has to restrict what goes in and out.

Affe
  • 47,174
  • 11
  • 83
  • 83
  • I was about the answer my own question after doing some intense research and you answered it perfectly. Yes it makes sense, what would happen if the return type didn't agree. Thank you for clarifying that. – rubixibuc Mar 19 '12 at 00:35
  • The only thing I disagree with is that it is treated as the declared type not the upper bound. When the method or class compiles the only casts that I know which occur are back to the parametrized type on function return. Everything else is implicitly converted to the upper bound and treated as so in the method because of type erasure. Please let me know if I'm right about this. – rubixibuc Mar 19 '12 at 00:39
  • Ya, that's what I was trying to say in my run-on sentence that I strung together somewhat poorly :) – Affe Mar 19 '12 at 01:26
1

Any MyClass<XXX> is a MyClass, but contrary to what you are saying, MyClass<Double> is not a subclass of MyClass<Object>.

You can find more about it if you search for erasure, like here.

Community
  • 1
  • 1
assylias
  • 321,522
  • 82
  • 660
  • 783
  • Isn't a raw type just the type with type erasure? Maybe I'm misunderstanding this? – rubixibuc Mar 19 '12 at 00:00
  • What is a raw type then? – rubixibuc Mar 19 '12 at 00:02
  • I read what that said and the documentation on oracle's web site. If the upper bound is Object isn't MyClass = MyClass? – rubixibuc Mar 19 '12 at 00:07
  • @rubixibuc Looks like you found answers. I would recommend that you read the [Java Language Specification](http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.8) on raw types if you want to dig deeper. – assylias Mar 19 '12 at 08:07