/**
 * JAVA泛型的使用
 * 定义:泛型的本质是参数化类型,就是说所操作的数据类型被指定为一个参数。
 * 
 * 定义泛型方法的规则
 * 1.所有泛型方法声明都有一个类型参数声明部分(由尖括号分割),该类型参数声明部分在方法返回类型之前。
 * 2.类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
 * 3.泛型方法的声明和普通方法一样,需要注意的是类型参数只能代表引用类型,不能是原始类型(像int,char,double,folat等)
 * 4.每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
 * 
 * 类型擦除的说明:泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。
 * 
 * 类型通配符上限和类型通配符下限
 * <? extends T>表示该通配符所代表的类型是T类型的子类。
 * <? super T>表示该通配符所代表的类型是T类型的父类。
 * 
 * 总结:
 * 我们发现,在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,
 * 即还是原来的最基本的类型(本实例中为Box),当然,在逻辑上我们可以理解成多个不同的泛型类型。究其原因,在于Java中的泛型这一概念提出的目的,
 * 导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。
 * 泛型信息不会进入到运行时阶段。
 * 对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
 * 
 * 
 * @author dyq
 *
 */
public class GenericTest {
    public static void main(String[] args) {
        /**
         * 泛型方法
         */
        //泛型方法的声明和普通方法一样,需要注意的是类型参数只能代表引用类型,不能是原始类型(像int,char,double,folat等)
        Integer[] intArr = {1,2,3,4,5};
        String[] strArr = {"aaa","bb","ccc"};
        printArray(intArr);
        printArray(strArr);
        
        /**
         * 泛型类
         */
        Person<String> person = new Person<String>("老板"); 
        System.out.println(person.getPosition());
        
        /**
         * 类型通配符
         */
        Person<Number> name = new Person<Number>(120); 
        Person<Integer> age = new Person<Integer>(200);
        getData(name);
        getData(age);
        /**
         * 类型通配符上限和类型通配符下限
         * <? extends T>表示该通配符所代表的类型是T类型的子类。
         * <? super T>表示该通配符所代表的类型是T类型的父类。
         */
        
    }
    /**
     * 泛型的通配符,类型通配符一般是使用?代替具体的类型参数
     * 
     * @param data
     */
    public static void getData(Person<? extends Number> data) {
        System.out.println(data.getPosition());
    }
    /**
     * 所有泛型方法声明都有一个类型参数声明部分(由尖括号分割),该类型参数声明部分在方法返回类型之前。
     * @param inputArray
     */
    public static <E> void printArray(E[] inputArray) {
        for(E value : inputArray) {
            System.out.printf( "%s ",value);
        }
        System.out.println();
    }
    /**
     * 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
     * @param data
     * @return
     */
    public static <E> E getName(Person<E> E) {
        return E.getPosition();
    }
    /**
     * 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
     */
    public static <E,T> E getSex(E a,T b) {
        if(b!=null) {
            return a; 
        }else {
            return null;
        }
    }
}
/**
 * 泛型类的声明
 */
class Person<T>{
    private T position;//职位
    public Person(T position) {
        this.position = position;
    }
    public T getPosition() {
        return this.position;
    }
}