Reputation: 5381
I'm running into peculiar behavior in terms of compile time errors with the following code (I'm using JDK7):
public class classA { public void foo( List<Object> o ){} }
public class classB<T>{ public void bar( List<Object> o ){} }
We consider the the following test object
List<String> o = new ArrayList<String>();
There is no way to get java to compile by passing o as a parameter to the method foo of class classA, and as far as I can figure, there shouldn't be.
Now say we're in the main method of classB and try to just call bar without instantiating an instance of classB to call it on. I might expect to get a non-static method can't be called from static context compilation error like I would if I tried to pull that in classA, but instead I get a conversion invocation error. That, makes sense - the types don't line up.
However if I try call bar from a nonstatic context, as in
ClassB b = new classB();
b.bar( o );
Java seems to forgive me for not lining up the types and runs the code no problem. I haven't done anything to fix the issue of typcasting, so why does Java let this code execute, where it wouldn't with classA?
Edit: In response to some questions. classA is given just for reference - it isn't supposed to compile and I don't expect it to, so I can't offer code that compiles with it. The code for classB that DOES compile and execute might be given by:
public class classB<T> {
public void bar( List<Object> o ){}
public static void main( String[] args ){
classB b = new classB();
List<String> o = new ArrayList<String>();
b.bar( o );
}
}
This code compiles and executes. The exact same code without the generic declaration in the class declaration line does not work. I understand type erasure, which someone eluded to, but how does it help, since T is not references in the method bar or the main code
Also, there are tons of ways to make this code better. I'm really just looking for an explanation of its behavior
Upvotes: 2
Views: 214
Reputation: 262494
This is a limitation in how generics are implemented.
They are opt-in.
classB b = new classB();
Here, you are opting out of generics, and you do get a warning.
Note: classB.java uses unchecked or unsafe operations.
When you opt-out of generic type checking, you don't get any of it, for the whole class, even for methods that do not use the bound type T
.
As @vandale points out, with generics turned off, you could even get the code compiled with
public void bar( List<Float> o );
If you do a
classB<Object> b = new classB<Object>();
it will not compile anymore.
Upvotes: 4
Reputation: 51711
When you define the class as
class ClassB<T>
but instantiate it as
new ClassB().bar(new ArrayList<String>());
you're actually using a raw type (without generics) version of it. If you notice the warning about the type safety; the method signature is bar(List)
instead of bar(List<Object>)
:
Type safety: The method bar(List) belongs to the raw type ClassB. References to generic type ClassB should be parameterized.
If you pass the parameterized type T
as, say, String
new ClassB<String>().bar(new ArrayList<String>());
it doesn't compile with the error (notice the method signature again)
The method **bar(List<Object>)** in the type ClassB<String> is not applicable for the arguments (ArrayList<String>)
Upvotes: 4
Reputation: 95489
Java still produces an "unchecked" warning, but it is able to compile because of erasure; that is, the type List<T> and List<E> use the same, non-generic underlying type in the Java virtual machine. You can think of generics as merely syntactic sugar for inserting casts at the call sites and for doing some additional checking, but in the bytecode that the compiler emits, it's as if T and E were replaced with Object everywhere, and so the compiler is able to treat this as a warning and not an error.
Upvotes: 0
Reputation: 2321
Try using this instead:
public void method(List<? extends Object> o) { /* body */ }
This way it will accept any list whose generic type argument is a descendent of Object, the way you want. :)
Upvotes: 0