当前位置:   article > 正文

面向对象之16:一文 彻底搞懂Java中的泛型、泛型的 使用细节 及 注意事项:_泛型对象

泛型对象

目录

what's Generic?

泛型的概念:

泛型的好处:

泛型类、泛型接口、泛型方法中常用的泛型标识符的意义释义:

常用的泛型标识符说明:

自定义泛型类(常用):

自定义泛型接口(常用):

自定义泛型类、泛型接口的继承与实现:

自定义泛型方法(常用):

泛型数组以及泛型对象:

创建泛型数组 方式一:

 创建泛型数组 方式2(这种方式 安全一些 ):

泛型对象:

泛型的使用细节以及注意事项(重点):

自定义泛型类及自定义接口的使用细节以及注意事项:

自定义泛型方法的使用细节以及注意事项:

泛型数组的使用细节以及注意事项:

泛型数组的使用细节:

                      5. 泛型数组的注意事项:

泛型对象注意事项:

泛型的通配符(常用):

泛型的上下限(常用):

泛型的上限通配:

泛型的下限通配:

关于泛型的通配符、上下限的细节说明:

泛型的通配符、上下限的使用场景:

泛型的 泛型擦除(编译时泛型擦除、运行时泛型擦除):

泛型的编译时擦除:

泛型的运行时擦除:

泛型擦除 的无限制 类型擦除:

泛型擦除 的有限制 类型擦除(即使用了 泛型的上限通配 )

泛型方法的 泛型擦除 :

泛型擦除前的桥接方法:

往 泛型指定为Double类型的List集合中添加非 Double 的实例:


what's Generic?

  • 泛型(广泛的类型):
    • 泛型就相当于是一个标签。形象一些就是:在生活中随处可见的类似于泛型的参考。比如:男卫生间门上贴的标签  --》 男 or boy ,女卫生间门上的标签 --》 女 or girl,能表示性别的标签 理解在Java中就叫做泛型。再 比如:垃圾分类的垃圾桶上面贴的标签,比如:厨余辣鸡、有害辣鸡、可回收辣鸡、其他辣鸡,……等等。
  • 泛型的声明格式:<泛型标识符[,泛型标识符2,……]>   这个 <>  因为像,也叫做钻石运算符。
  • Java中 泛型的支持:泛型只支持引用数据类型,不支持基本数据类型!!!!
  • 泛型的概念:

    • 集合(有的叫容器)类在设计阶段/声明阶段,并不能够确定这个容器 实际应该存入什么样类型的对象。所以在 JDK1.5 之前都只能把元素的类型设计为Object。这样在使用集合的时候。如果想使用添加的元素的类型的特有成员时。就难免会使用到 类 的向下转型!所以很有可能引发 ClassCastException 异常!
    • JDK1.5 开始 就使用泛型来解决。因为这个时候除了集合中的元素类型不确定。其他的部分都是确定的。比如关于这个元素应该怎么存储。怎么进行管理这些逻辑是 集合中的API 已经实现了逻辑了的。所以这时把元素的类型 设计成为一个参数(泛型标识符)的话。这个类型 就叫做泛型!在实例化集合类的时候。当开发者指定为 什么 数据类型,那么集合中所有使用到 泛型标识符 的地方都会在 编译时成为 开发者指定的数据类型!从而就可以直接在实例化的地方调用 该类型特有和继承、实现下来的成员!
    • ·Java中泛型(Generic)是在 JDK1.5 引入的新特性。泛型的主要作用就是提供了 编译时类型 的安全监测机制。该机制允许开发者在编译时 检测到非法的类型数据结构,即指定了什么数据类型,就只能操作什么数据类型的成员!!
      • 泛型的本质就是参数化类型。换言之,就是所操作的数据类型被指定为一个参数!当使用泛型类的时候,开发者给定了什么类型,那么泛型类 的 泛型标识符所表示的数据类型就是 该类型!从而就可以直接使用、 该类型特有和继承、实现下来的成员!
  • 泛型的好处:

    • Java泛型可以在编译阶段 保证  数据的安全性。比如在集合类中的体现。 当泛型指定为 List<String> list = new ArrayList<>(); 当调用 list.add() API 的时候,就只能往集合中添加String类型的 实例。
    • 消除了 类 的向下转型 。消除了 有可能引发的 ClassCastException 异常的产生!同时,代码更加简洁、健壮。
    • 在Java的源码中、各种设计模式、框架中 泛型得到广泛的应用!

泛型类、泛型接口、泛型方法中常用的泛型标识符的意义释义:

  • <E> : E ---> Element(元素) 的首字母缩写。表示为类中的元素的泛型(比如表示为:实例变量、常量 的泛型,在Java中的List集合、Set集合中经常会看到这样的泛型)。
  • <T>:T ---> Type(类型)的首字母缩写。表示为类中的泛型 "任意引用数据类型"。(比如表示为:类中的实例成员方法、实例变量、常量的泛型)。
  • <K>:K ---> Key(键)的首字母缩写。表示为类中的 键的数据类型。(在Java中的Map集合中 经常会看到这样的泛型)
  • <V>:V ---> Value(值)的首字母缩写。表示为类中的 值的数据类型。(在Java中的Map集合中 经常会看到这样的泛型)
  • <S>:S --->等同于 T  的意义。可以理解成 Species (种类、物种) 的首字母缩写。表示为类中的泛型 "任意引用数据类型。 表示为类中的泛型 "任意引用数据类型"
  • <U>:U ---> 等同于 T  的意义。不是什么单词首字母的缩写。如果非要解释那就是 U的字母顺序 离 T 比较近。表示为类中的泛型 "任意引用数据类型"
  • <R>:R ---> 等同于 T  的意义。不是什么单词首字母的缩写。如果非要解释那就是 R的字母顺序 离 T 比较近。表示为类中的泛型 "任意引用数据类型"
  • <Q>:Q ---> 等同于 T  的意义。不是什么单词首字母的缩写。如果非要解释那就是 Q的字母顺序 离 T 比较近。表示为类中的泛型 "任意引用数据类型"
  • <A>:   A ---> 等同于 T  的意义。不是什么单词首字母的缩写。可以理解成 a (一、任一、每个、每一) 的冠词的大写。表示为类中的泛型 "任意引用数据类型"
  • <C>:C ---> 等同于 T  的意义。可以理解成为Class(类),Category(种类、分类)的首字母缩写。表示为类中的泛型 "任意引用数据类型"
  • <G>:G ---> 等同于 T  的意义。可以理解成 Genre (类型) 的首字母缩写。表示为类中的泛型 "任意引用数据类型"
  • 常用的泛型标识符说明:

    • 其实泛型的标识符不仅仅局限于上面 我所举例的常用的泛型标识符。泛型标识符可以是 任意的英文字母 大写。甚至是 英文单词 都可以的!
    • 只是为了避免歧义。所以才会有这种约定俗成的 命名规则。
    • 基本上不会使用英文单词来作为泛型标识符。
      • 如果真的拿英文单词来作为泛型表示符的话。如果按照 小驼峰命名法 。和变量名、方法名 就产生了歧义。
      • 如果是 按照 大驼峰命名法。和类名 就产生了歧义。
      • 如果是 全部大写。和常量名 就产生了歧义。
      • 应该避免 产生歧义。而不是制造歧义!

自定义泛型类(常用):

例如:

  1. class Son<T> {
  2. private T t;
  3. private T[] tArr;
  4. public final List<T> list = new ArrayList<>();
  5. public void setT(T t) {
  6. this.t = t;
  7. }
  8. public T getT() {
  9. return t;
  10. }
  11. public List<T> getList(){
  12. return list;
  13. }
  14. }

test:

  1. ,此时会看到在编译阶段(也就是写代码时),set方法的数据类型就行在 写泛型类的时候 开发者指定的数据类型。达到了数据类型的安全性!
  2. ,此时会发现,在getT返回设置的属性的时候,就已经是开发者给定的数据类型。从而就可以直接调用String类中的API,就不用再进行类型的强制转换!
  3. ,而get到 List集合的时候,此时的 add方法中的入参类型已经变成了 在一开始实例化对象的时候。就已经给定了的 数据类型!
  4. 另外在集合中的表现是一回事,比如:

自定义泛型接口(常用):

例如:

  1. interface Person<T,S,R,U,Q>{
  2. S s();
  3. Map<T,R> setMap(U u,Q q);
  4. default void print(T t,S s){
  5. System.out.println(t.getClass());
  6. System.out.println(s.getClass());
  7. }
  8. }

test:和自定义泛型类是一样的使用,只是接口的泛型是 它的实现类在初始化时、定义时 或者是 子接口定义时、或者是 子抽象类定义时指定的!

 


自定义泛型类、泛型接口的继承与实现:

形式1(稍微复杂些的)(不常用):

  1. interface Person<Q> {
  2. public abstract void personMethod(Q q);
  3. }
  4. // 不给定父接口泛型
  5. interface SubPerson<Q, C> extends Person<Q> {
  6. default void subMethod(C c) {
  7. System.out.println(c.getClass());
  8. }
  9. }
  10. // 给定父接口泛型,同时声明自己类中的泛型
  11. abstract class AbstractPerson<T, U, S> implements SubPerson<List<String>, HashMap<String, String>> {
  12. @Override
  13. public void personMethod(List<String> list) {
  14. list.add("抽象类向集合中添加元素");
  15. }
  16. public abstract ArrayList<Object> personMethod(T t, U u);
  17. protected void method2(S s) {
  18. System.out.println(s.getClass());
  19. }
  20. }
  21. // 给定父接口的第三个泛型为 Scanner 类型
  22. // 其他两个泛型在实例化 Coder的时候再指定。
  23. // Coder再声明一个自己的泛型。
  24. class Coder<R, T, U> extends AbstractPerson<T, U, Scanner> {
  25. private T t;
  26. private U u;
  27. public Coder(T t,U u){
  28. this.t = t;
  29. this.u = u;
  30. }
  31. public T getT(){
  32. return t;
  33. }
  34. public U getU(){
  35. return u;
  36. }
  37. @Override
  38. public ArrayList<Object> personMethod(T t, U u) {
  39. ArrayList<Object> list = new ArrayList<>();
  40. Collections.addAll(list, t, u);
  41. return list;
  42. }
  43. public Double useR(R r) {
  44. if (r instanceof Integer) {
  45. return Double.valueOf(calculate((Integer) r, 100));
  46. } else {
  47. System.out.println(r.getClass());
  48. }
  49. return Math.PI;
  50. }
  51. private int calculate(int start, int target) {
  52. return (start + target) * (target / 2);
  53. }
  54. }

test:

  1. public static void testGenericExtends() {
  2. Coder<Integer, String, Double> coder =
  3. new Coder("第二个泛型为String类型",Math.E);
  4. coder.method2(new Scanner(System.in));
  5. // 直接以实例化时给定的泛型为准
  6. String t = coder.getT();
  7. System.out.println(t);
  8. Double u = coder.getU();
  9. System.out.println(u);
  10. coder.personMethod(new ArrayList<>());
  11. ArrayList<Object> objects = coder.personMethod("圆周率:", Math.PI);
  12. System.out.println(objects);
  13. Double result = coder.useR(1);
  14. System.out.println(result);
  15. }

 

形式2(什么泛型都不在定义泛型类、泛型接口的时候 给定)(常用)

  • 说明:
  • 如果子类、子接口、实现类什么类型都不在定义指定的话。那该类、该接口 就是成为泛型类、泛型接口。并且泛型标识符要和父类、父接口的一模一样!
  • 其实子类、子接口、实现类 可以更改父类、父接口的泛型标识符的,但是更改了之后!在定义自己类、接口的时候,也要是这个标识符!(一般不会这样做!)
  • 如果子类自己有拓展的标识符的话,那么和父类泛型标识符一样的那个标识符 在子类自己定义类 声明泛型标识符的时候,和父类泛型标识符一样的那个标识符可以放在泛型声明的 钻石运算符中的任意位置 。只是这可能会产生不好理解的情况。
    1. interface People<T> {
    2. public abstract Integer peopleMethod(T t);
    3. }
    4. interface Man<T, S> extends People<T> {
    5. @Override
    6. Integer peopleMethod(T t);
    7. default String m1(S s, T t) {
    8. return s.getClass().toString() + "和" + t.getClass().toString();
    9. }
    10. }
    11. abstract class Dad<T, S> implements Man<T, S> {
    12. protected T t;
    13. protected Dad(T t) {
    14. this.t = t;
    15. }
    16. @Override
    17. public Integer peopleMethod(T t) {
    18. return (Integer) t + 999999;
    19. }
    20. }
    21. class Child<T, S, L> extends Dad<T, S> {
    22. private S s;
    23. public Child(T t, S s) {
    24. super(t);
    25. this.s = s;
    26. }
    27. public S getS() {
    28. return s;
    29. }
    30. public List<? extends L> getList() {
    31. return new LinkedList<>();
    32. }
    33. }

    test:

    1. public static void main(String[] args) {
    2. Child<Integer, String, Serializable> child = new Child<>(9090, "Created at 2021-06-14 08:59");
    3. String s = child.getS();
    4. System.out.println(s);
    5. Integer integer = child.peopleMethod(789);
    6. System.out.println(integer);
    7. Integer t = child.getT();
    8. System.out.println(t);
    9. List<? super Serializable> list = child.getList();
    10. list.add(new Date());
    11. list.add(new java.sql.Date(System.currentTimeMillis()));
    12. list.add(new ArrayList<Timestamp>().add(null));
    13. ArrayList<Long> currentTimeMillis = new ArrayList<>();
    14. currentTimeMillis.add(new GregorianCalendar().getTime().getTime());
    15. System.out.println(list);
    16. System.out.println(currentTimeMillis);
    17. System.out.println(child.m1(new String(), Integer.MAX_VALUE));
    18. }

    test:


自定义泛型方法(常用):

  1. 泛型方法声明格式:方法修饰符 <泛型标识符> 返回值类型 方法名(泛型 形参名,[其他数据类型 形参名]){return 返回值;}
    1. public class GenericTest3 {
    2. public static void main(String[] args) {
    3. Generic<Generic> generic = new Generic<>();
    4. Generic generic1 = generic.isNotGenericMethod(generic);
    5. System.out.println(generic);
    6. System.out.println(generic1);
    7. String s = generic.isGenericMethod("是String类型");
    8. System.out.println(s);
    9. ArrayList<Date> dates = new ArrayList<>();
    10. List<ArrayList<Date>> listInstance = Generic.getListInstance(dates);
    11. listInstance.add(dates);
    12. System.out.println(listInstance.getClass());
    13. }
    14. }
    15. class Generic<T> {
    16. private T t;
    17. public Generic(){}
    18. // 此方法并不是泛型方法
    19. public T isNotGenericMethod(T t) {
    20. return isNotGenericMethod2(t);
    21. }
    22. // 此方法也不是泛型方法
    23. private T isNotGenericMethod2(T t) {
    24. T tt = null;
    25. try {
    26. // 通过这样的反射方式 创建本类对象,构造器必须显示初始化才可以!
    27. tt = (T) t.getClass().getConstructor().newInstance();
    28. } catch (Exception e) {
    29. e.printStackTrace();
    30. }
    31. return tt;
    32. }
    33. // 是泛型实例方法
    34. public <S> S isGenericMethod(S s){
    35. if ("是String类型".equals(s)){
    36. return s;
    37. }
    38. return null;
    39. }
    40. // 是泛型静态方法
    41. public static <U> List<U> getListInstance(U u){
    42. if (u instanceof List){
    43. return new LinkedList<>();
    44. }
    45. return null;
    46. }
    47. }
    result:,泛型在编译时的数据类型:

泛型数组以及泛型对象:

创建泛型数组 方式一:

  1. class Generic2<G> {
  2. private G[] gArr;
  3. private Object obj;
  4. public <T> Generic2(T t) {
  5. // 多态的引用!
  6. obj = t;
  7. // 创建泛型数组的方式1:
  8. // (如果在实例化Generic2的时候,给定的泛型不是 被强转的类型时就会
  9. // 引发并抛出 ClassCastException 异常,
  10. // 因为向下转型的前提是要
  11. // 父类引用指向子类对象时。
  12. // 这时候的向下转型到子类类型的时候 才不会抛出 ClassCastException
  13. // (G[]) new Object[5] 如果这里的向下转型在实例化Generic2的时候
  14. // 指定的泛型如果不是Object时,是其他类的时候,就会抛出 ClassCastException异常
  15. // 因为要清楚的知道的是 new Object[5] 这个动作是在 初始化 Object类型的
  16. // 数组,而不是在初始化 泛型的数组,泛型的数组不能够被初始化!
  17. // 因为泛型是未知的数据类型,怎么初始化?)
  18. gArr = (G[]) new Object[5];
  19. }
  20. public G[] getgArr(){
  21. return gArr;
  22. }
  23. public Object getObj(){
  24. return obj;
  25. }
  26. }

 test1:使用泛型数组是,引发的ClassCastException异常情况:


 

 创建泛型数组 方式2(这种方式 安全一些 ):

  1. class Generic2<G> {
  2. private G[] gArr;
  3. private Object obj;
  4. public <T> Generic2(T t) {
  5. // 多态的引用!
  6. obj = t;
  7. // 创建泛型数组的方式1:
  8. // (如果在实例化Generic2的时候,给定的泛型不会 被强转的类型时就会
  9. // 引发并抛出 ClassCastException 异常,
  10. // 因为向下转型的前提是要
  11. // 父类引用指向子类对象时。
  12. // 这时候的向下转型到子类类型的时候 才不会抛出 ClassCastException
  13. // (G[]) new Object[5] 如果这里的向下转型在实例化Generic2的时候
  14. // 指定的泛型如果不是Object时,是其他类的时候,
  15. // 如果在类的外部获取 G[] 这个泛型数组,就会抛出 ClassCastException异常
  16. // 因为要清楚的知道的是 new Object[5] 这个动作是在 初始化 Object类型的
  17. // 数组,而不是在初始化 泛型的数组,泛型的数组不能够被初始化!
  18. // 因为泛型是未知的数据类型,怎么初始化?)
  19. // 如果这里用了 (G[]) new Object[5] 其本质就是在使用 Object类型的数组!
  20. // 这种方法没有任何意义。要么就直接使用 Object 的数组,没有必要转来转去。
  21. gArr = (G[]) new Object[5];
  22. }
  23. public G[] initializeGArr(Class<? super G> clazz, int initLength) {
  24. // 创建泛型数组的方式2:通过反射包下( java.lang.reflect.Array;)的 Array类的newInstance创建数组对象
  25. /* public static Object newInstance(Class<?> componentType, int length)
  26. throws NegativeArraySizeException {
  27. return newArray(componentType, length);
  28. }
  29. 这个API的本质仍是返回的Object类型。)(newArray底层是一个本地方法,由C或者C++实现)
  30. 而引用数据类型的数组都继承自 Object 和 Object[]
  31. 基本数据类型的一维数组只继承自 Object,它们并没有继承自 Object[]
  32. 因为基本数据类型并不是引用数据类型!
  33. (需要说明的是,基本数据类型
  34. 二维数组继承自 Object 、Object[]
  35. 三维数组继承自 Object 、Object[] 、 Object[][]
  36. 依次类推……
  37. 引用数据类型
  38. 二维数组继承自 Object、Object[] 、Object[][]
  39. 三维数组继承自 Object 、Object[] 、 Object[][] 、Object[][][]
  40. 依次类推……)
  41. */
  42. gArr = (G[]) Array.newInstance(clazz, initLength);
  43. return gArr;
  44. // return (G[]) Array.newInstance(clazz, initLength);
  45. }
  46. public G[] getgArr() {
  47. return gArr;
  48. }
  49. public Object getObj() {
  50. return obj;
  51. }
  52. }
  53. /*
  54. 使用反射创建泛型数组的对象的时候方法签名中接收Class对象时
  55. 可以有的形式:它们四种方式都有可能引发 ClassCastException!!! 不是绝对安全的!
  56. 第一种:使用 泛型通配符:
  57. public G[] initializeGArr(Class<?> clazz, int initLength)
  58. 第二种:使用 泛型下限通配:
  59. public G[] initializeGArr(Class<? super G> clazz, int initLength)
  60. 第三种:使用 泛型上限通配:
  61. public G[] initializeGArr(Class<? extends G> clazz, int initLength)
  62. 第四种:就是不使用泛型(泛型擦除):
  63. public G[] initializeGArr(Class clazz, int initLength)
  64. */

test:

  1. public static void main(String[] args) {
  2. Generic2<HashMap<String, String>> generic2 = new Generic2<>(123);
  3. HashMap<String, String>[] hashMaps = generic2.initializeGArr(HashMap.class, 3);
  4. HashMap<String, String> map = new HashMap<>();
  5. map.put("first", "Array.newInstance(Class<?> componentType, int length)通过反射创建泛型数组");
  6. hashMaps[0] = map;
  7. for (int i = 0; i < hashMaps.length; i++) {
  8. System.out.println(hashMaps[i]);
  9. }
  10. System.out.println("hashMaps.length = " + hashMaps.length);
  11. System.out.println("hashMaps.getClass() = " + hashMaps.getClass());
  12. Object obj = generic2.getObj();
  13. System.out.println("obj.getClass() = " + obj.getClass());
  14. }

result:

 

泛型对象:

  1. public static void main(String[] args) {
  2. Generic2<Generic> Generic2 = new Generic2<>();
  3. Generic<Object> generic = new Generic<>();
  4. Generic instance = Generic2.getGInstance(generic);
  5. System.out.println(generic.toString());
  6. System.out.println(instance.toString());
  7. String str = Generic2.getInstance(String.class);
  8. System.out.println(str.getClass().getSuperclass().getSimpleName());
  9. }
  10. // 一把都不这样创建 类的泛型的对象。
  11. // 直接在外部使用的时候创建就好了,
  12. // 为什么在泛型类中创建?
  13. public G getGInstance(G g) {
  14. return reflectNewInstance(g);
  15. }
  16. private G reflectNewInstance(G g) {
  17. try {
  18. g = (G) g.getClass().getConstructor().newInstance();
  19. } catch (Exception e) {
  20. }
  21. return g;
  22. }
  23. // 可以使用泛型方法创建其他类的对象!
  24. /* 前提是这个类本身就要能够创建对象*/
  25. public <T> T getInstance(Class<T> c) {
  26. T t = null;
  27. try {
  28. t = c.getConstructor().newInstance();
  29. } catch (Exception e) {
  30. }
  31. return t;
  32. }
  33. public Generic2() {
  34. }

result:


泛型的使用细节以及注意事项(重点):

  • 自定义泛型类及自定义接口的使用细节以及注意事项:

    1. 不管是使用 自定义的泛型类还是用别人写的泛型类。在实例化泛型类的时候,在有 数据类型 对象名 引用的情况下。都不用再指定泛型。在JDK1.7之前需要手动指定。到时再JDK1.5之后,编译器会自行推断。比如:
    2. 泛型类的实例成员(实例变量、常量、方法、代码块、内部类)中可以直接使用类的泛型!
    3. 类的静态成员(静态变量、常量、方法、代码块、静态内部类、静态内部枚举、静态内部接口、静态内部注解)不能够使用 类的 泛型。因为泛型是属于类的实例的,静态方法是属于类的,并不是属于类的实例的。静态成员加载时还没有创建对象。所以不能够使用类的泛型。
    4. 泛型类在继承类的时候,这个父类不一定要是 泛型类。可以是一个普通的类。在继承、实现泛型接口的时候,这个父接口不一定要是个 泛型接口,可以是一个普通接口。
    5. 泛型类在继承泛型父类 or 实现接口、包括泛型子接口继承泛型父接口的时候,如果泛型类在定义的时候,没有给 泛型父类 or 泛型父接口给定具体数据类型的话,那么这个泛型类、泛型子接口 的 泛型声明 也要是和泛型父类、泛型父接口的泛型声明一样!
    6. 泛型类在继承泛型父类 or 实现接口的时候,泛型类在定义的时候,可以在继承 or 实现时 更改泛型父类 or  泛型父接口的 泛型标识符 ,此时泛型类的 泛型声明的泛型标识符要和自己修改的一样!(一般很少这样操作)子泛型接口 继承 父泛型接口也是一样!
    7. 泛型类在继承泛型父类 or 实现泛型接口的时候,泛型类在定义的时候,泛型类的泛型声明的泛型标识符的顺序可以和 泛型父类 or 泛型父接口的不一致。但是这样很容易搞混淆(基本上 不会把顺序搞得不一样。)子泛型接口 继承 父泛型接口也是一样!
    8. 一个普通类 也可以继承、实现 泛型父类、泛型父接口、一个 普通接口 也可以继承 泛型父接口。但是前提是 在 继承 or 实现的时候,要给 泛型父接口、泛型父类给定具体的数据类型!
    9. 泛型类在继承或者实现时,泛型接口在继承时。可以拓展自己的泛型声明!可以有 0 ~ n个泛型声明!
    10. 泛型类的构造器写法和普通类的构造器写法是一样的,泛型类的构造器后面没有<>这个符号。比如:,不会因为这个类是个泛型类,构造器的写法就有所改变。构造器如果想使用自己独有的泛型的话,那么这个构造器可以定义成为泛型构造器。
    11. 泛型类中的静态变量 以及 静态常量 不能使用 泛型!类中的静态成员有 静态方法、静态内部类、静态内部接口 可以使用自己的泛型!静态的成员不能够使用类的泛型!
    12. 如果在 定义 泛型类 的时候 没有给泛型父类和泛型父接口给定具体类型的话。那么在实例化这个 泛型类的时候,尽量给定具体的数据类型。不然就会 发生编译时的 泛型擦除。即 所有的 泛型标识符 表示的数据类型 都会泛型擦除为 Object 类型。(要么所有的泛型都不给定具体数据类型。要么全部泛型都要给定具体数据类型,没有给定几个和不给定几个的操作!)
    13. 泛型类的泛型是在 泛型类继承 泛型父类、实现 泛型接口 或者是 实例化泛型类的时候 给定具体数据类型。如果说 在 定义时和实例化时都没有给定具体的数据类型。那么这些泛型声明就啥作用都没有,全部泛型擦除为Object类型
    14. 自定义异常类不可以是泛型类,编译器不支持。但是 异常类中可以有 泛型方法。
    15. 自定义枚举类不可以是泛型类,编译器不支持。但是 枚举类中可以有 泛型方法。
    16. 自定义注解不可以是泛型注解,编译器不支持。

  • 自定义泛型方法的使用细节以及注意事项:

    1. 泛型方法可以分为 泛型静态方法 和 泛型实例方法 和 泛型构造器 。
    2. 泛型方法必须要有 <泛型标识符> 泛型声明(钻石运算符 ) !不然这个方法就不是泛型方法!如果是实例方法,那么只能说这个方法使用了类的泛型。而它本身不是泛型方法。
    3. 泛型方法   可以出现在 普通类、普通抽象类、普通接口、泛型类、泛型抽象类、泛型接口、枚举类、自定义异常类、静态内部类、局部内部类(只能有泛型实例方法)、实例内部类(只能有泛型实例方法)、匿名内部类(只能有泛型实例方法)
    4. 泛型方法的重载注意事项(不能是相同的泛型声明,这和是不是相同的泛型标识符无关,因为它们 泛型声明的 泛型擦除后的 类型都是Object,所以不构成泛型方法的重载!):构造泛型方法的重载就要满足方法重载的必要条件,即(方法名必须相同、参数类型不同、或者 参数类型个数不同 或者参数类型顺序不同 (和是不是静态方法、实例方法无关!)泛型方法重载的坑:这样 重载实例方法的时候 虽然在编译时通过了编译,倒是如果在调用的时候,传入了String类型的话。这个时候,编译器就懵逼了。比如引发编译不通过的错误:,解决方法:要么实例化泛型类的时候就不要指定泛型为 和已有的重载方法形参类型相同 ,要么就把 已经给定了的数据类型的那个重载方法改成其他数据类型!比如:
    5. 泛型方法的泛型是独立于类而存在的,类、接口的泛型是以类、接口为单位!而方法的方法是以方法为单位!即 即使泛型方法的泛型声明的泛型标识符和类、接口的泛型标识符是一样的。也是独立开来的,这并影响 泛型方法的调用。为了避免歧义。所以 Java规定了要 写 泛型声明 !
    6. 泛型方法的泛型声明 必须在返回值的前面、方法修饰符的后面!
    7. 泛型实例方法、泛型构造器 既可以使用方法、构造器 自己本身的泛型,也可以使用 类的泛型。而泛型静态方法只能够使用 泛型静态方法自己本身 的泛型!
    8. 泛型方法的泛型是在调用方法时 给定具体的 数据类型 的,即调用泛型方法时,传入了 什么引用数据类型的实例(即 实参)调用方法,即 这个泛型表示符 所表示的 数据类型就是 这个实参的 数据类型。
    9. 而 (泛型类泛型接口的泛型 可以是在 类、泛型类 继承 泛型父类,实现泛型父接口的时候 给泛型父类、泛型父接口 给定具体的数据类型)(一般都不会在继承、实现是给定 引用数据类型,只是说可以zheyan)。如果没有给定,那么这个泛型类的 泛型标识也要和 泛型父类、泛型父接口的泛型标识一样。如果没有在继承 or 实现的时候给定 泛型父类、泛型父接口具体的数据类型。那么在实例化 泛型类 的时候,也可以 给定 泛型的具体类型!

  • 泛型数组的使用细节以及注意事项:

    1. 创建泛型数组的方式1: 比如:T[] tArr = (T[]) new Object[5] ;  如果在 实例化泛型类的时候,给定的泛型不是 被强转的类型时 就会 引发并抛出 ClassCastException 异常。这是非常危险的操作。如果是用Object[]来接的话,会发现没报错。那是因为 new Object[5]  这个操作确实就是 初始 Object的数组!!而在运行时,泛型是被擦除了的(泛型只保留在编译阶段)!!!具体细节在上面已经在  泛型数组以及泛型对象:阐明清楚了
      声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/810343
推荐阅读
相关标签