简体   繁体   中英

Type inference in generic method & generic class

Having trouble understanding generic programming in Java.

I read some tutorial about it but still quite confused, especially when things get complicated.

Can anyone explain what's happening in this example?

import java.util.Date;

public class Test1 {

    public static void main(String[] args) {
        P<Cls> p = new P<>();   //<1>   //I expect a ClassCastException here, but no. Why? //How does the type inference for class P<E> work? 
        System.out.println(p.name); //it prints
//      System.out.println(p.name.getClass());//but this line throws ClassCastException //why here? why not line <1>?

        test1(p);//it runs
//      test2(p);//throws ClassCastException//What is going on in method test1&test2? 
                //How does the type inference for generic methods work in this case?
        }


    public static<T> void test1(P<? extends T> k){
        System.out.println(k.name.getClass());
    }

    public static<T extends Cls> void test2(P<? extends T> k){
        System.out.println(k.name.getClass());
    }
}

class P<E>{
    E name = (E)new Date();//<2>
}

class Cls{}
P<Cls> p = new P<>();

Remember that Java implements generics by erasure , meaning that the constructor for P doesn't really have any idea what E is at runtime . Generics in Java are purely there to help developers at compile-time.

This means that when you create a new P<>() , a new Date() is created, but it isn't actually cast to any particular type, because the runtime doesn't know anything about E . E does not exist at runtime, as far as the P<E> class is concerned. name is just an Object reference that has a Date inside. However, any time you write code that consumes name in a way where the runtime environment needs to know that it's of a particular type ( Cls in this case), the compiler inserts a cast to that type without telling you.

  • p.name.getClass() gets compiled as ((Cls)p.name).getClass() , which will create a class cast exception.
  • test2() specifies a type constraint that is not generic ( extends Cls ). So its call to p.name.getClass() likewise is translated to ((Cls)p.name).getClass() .

On the other hand:

  • System.out.println(p.name) is actually the same as System.out.println((Object)p.name) because println is a non-generic method that takes an object .
  • test1(p.name) is similar. Because the runtime doesn't actually know what type T is, it basically casts p.name as Object before calling getClass() on it.

In other words, here's your code as it actually gets compiled:

class P{
    Object name = new Date();
}

public static void main(String[] args) {
    P p = new P();   
    System.out.println(p.name);
    System.out.println(((Cls)p.name).getClass());

    test1(p);
    test2(p);
}


public static void test1(P k){
    System.out.println(k.name.getClass());
}

public static void test2(P k){
    System.out.println(((Cls)k.name).getClass());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM