4

I am confused with this code, which is supposed not to work (I guess), but it seems to work with no errors.

When I put <T> next to class Person, the wildcard in Arraylist don't behave as it is defined, in the code below, the wildcard should only work for super Person class, but with the <T> next to the class Person, it accept all kind for types (in this example it's String). And also for this to work, Human when defined should not have the type specified. Here's the code:

import java.util.ArrayList;

public class Person {
    public static void main(String[] args) {
        Human h = new Human();
        h.al(new ArrayList<String>()); // this should give an error no ?
    }
}

class Human<T>{ //Human is a generic class, but above I created an instance without specifying the T
    public void al(ArrayList<? super Person> a){ //the type passed should be a super of Person

    }
}

Thank you :)

CMPS
  • 7,505
  • 4
  • 26
  • 49

2 Answers2

4

This is answered in the Java Generics FAQ: Can I use a raw type like any other type?

The answer says:

Methods or constructors of a raw type have the signature that they would have after type erasure.

In English, it means that because you declared h as a raw type Human, the compiler will perform type erasure on h and the effective signature of the al method is actually the one after the type erasure! So as far as the compiler can tell, your code is sane.

Also see the answers to this question: Java generics - type erasure - when and what happens for some more examples.

Wikipedia also has a good section on some of the Problems with Type erasures that might be tricky to catch.

UPDATE

The snippet below will throw a ClassCastException at runtime.

import java.util.ArrayList;
import java.util.Iterator;

class Human<T>{
  public void al(ArrayList<? super Person> a){
    Iterator<? super Person> iter = a.iterator();
    while (iter.hasNext()) {
      Person h = (Person) iter.next(); // unsafe cast
    }
  }
}

public class Person {
  public static void main(String[] args) {
    Human h = new Human();

    ArrayList<String> arr = new ArrayList<String>();
    arr.add("deadbeef");

    h.al(arr); // valid, since h is raw
  }
}
Community
  • 1
  • 1
MEE
  • 1,533
  • 9
  • 15
  • The eraser causes a unchecked warning, but this is at compile time no ? What about runtime, shouldn't it cause an error when running the program ? – CMPS Apr 29 '14 at 15:54
  • Yes, it can cause an exception at runtime. But that depends on what the code really does. I updated the answer with an example. Thanks. – MEE Apr 29 '14 at 16:52
  • @Amir, What kind of "details" are you looking for? – MEE Apr 30 '14 at 04:31
  • John thank you for your answer, I opened a bounty for this question to find a more detailed explanation, but your answer is the best for now. – CMPS Apr 30 '14 at 17:54
  • It's not really clear what more detailed explanation you need. The erasure of `Human.al` is `public void al(ArrayList a)`... – Jon Skeet May 01 '14 at 23:03
0

If you use a generic parametrised class without <> the generic typing is disabled for that class, for that variable. If you do:

Human<Object> h = new Human<>();

then a compiler error would be raised.

Landei
  • 52,346
  • 12
  • 89
  • 188
Joop Eggen
  • 96,344
  • 7
  • 73
  • 121
  • AFAIK <> diamond operator is a part of java 7, Link to the Original Proposal http://mail.openjdk.java.net/pipermail/coin-dev/2009-February/000009.html So the compile error would not occur for the version 1.7 and up. – Arun A K Apr 28 '14 at 06:05
  • 2
    The diamond operator is irrelevant here, pre-1.7 you could write `Human human = new Human();` with the same result. – Landei Apr 28 '14 at 07:48
  • @Landei thanks for the clarification: indeed the problem here is having `Human h` instead of `Human`. – Joop Eggen Apr 28 '14 at 19:01
  • Yes if you specify the type `` the warning disappear, but my question is why it disappear ? What is the rule I'm missing ? – CMPS Apr 29 '14 at 16:30