Java泛型

介绍

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

泛型标记符

  • E - Element (在集合中使用,因为集合中存放的是元素)
  • T - Type(Java 类)
  • K - Key(键)
  • V - Value(值)
  • N - Number(数值类型)
  • - 表示不确定的 java 类型

优点

  • 避免了类型强转的麻烦。
  • 它提供了编译期的类型安全,确保在泛型类型(通常为泛型集合)上只能使用正确类型的对象,避免了在运行时出现ClassCastException。

泛型类

泛型类的定义语法
class 类名称 <泛型标识,泛型标识,…> {
  private 泛型标识 变量名; 
  .....
}

定义语法

// Java1.7以后,后面的<>中的具体的数据类型可以省略不写,如:类名<具体的数据类型> 对象名 = new 类名<>();
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
泛型类注意事项
  • 泛型类,如果没有指定具体的数据类型,此时,操作类型是Object

  • 泛型的类型参数只能是类类型,不能是基本数据类型

  • 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型

泛型类派生子类(泛型继承)

子类是泛型类,子类和父类的泛型类型要一致

class ChildGeneric<T> extends Generic<T> 

子类不是泛型类,父类要明确泛型的数据类型

class ChildGeneric extends Generic<String>
使用示例
/**
 * 此处的T可以随便写为任意标识,常见的有T、E等形式的参数表示泛型,泛型在定义的时候不具体,使用的时候才变得具体。在使用的时候确定泛型的具体数据类型。即在创建对象的时候确定泛型。
 */
public class GenericsClassDemo<T> {
 //t这个成员变量的类型为T,T的类型由外部指定
 private T t;

 //泛型构造方法形参t的类型也为T,T的类型由外部指定
 public GenericsClassDemo(T t) {
 this.t = t;
 }
 //泛型方法getT的返回值类型为T,T的类型由外部指定
 public T getT() {
 return t;
 }
}


// 
// 例如:

当你的泛型类型想变为Integer类型时,也是很方便的。直接在创建时,T写为Integer类型即可:
Generic<Integer> genericInteger = new Generic<Integer>(666);

泛型在定义的时候不具体,使用的时候才变得具体。

// 泛型在定义的时候不具体,使用的时候才变得具体,在使用的时候确定泛型的具体数据类型。即:在创建对象的时候确定泛型。
Generic<String> genericString = new Generic<String>("helloGenerics");
// 此时,泛型标识T的类型就是String类型,那我们之前写的类就可以这么认为:
public class GenericsClassDemo<String> {
 private String t;

 public GenericsClassDemo(String t) {
 this.t = t;
    }
 public String getT() {
 return t;
    }
}

泛型接口

泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中。

泛型接口的定义语法
interface 接口名称 <泛型标识,泛型标识,…> {
  泛型标识 方法名(); 
  .....
}
使用格式

定义类时确定泛型的类型

public class GenericsImp implements GenericsInteface<String> {
 @Override
 public void add(String s) {
 System.out.println("设置了泛型为String类型");
    }
}

始终不确定泛型的类型,直到创建对象时,确定泛型的类型

public class GenericsImp<T> implements GenericsInteface<T> {
 @Override
 public void add(T t) {
 System.out.println("没有设置类型");
    }
}

确定泛型

public class GenericsTest {
 public static void main(String[] args) {
 GenericsImp<Integer> gi = new GenericsImp<>();
 gi.add(66);
    }
}

泛型方法

泛型方法,是在调用方法的时候指明泛型的具体类型

语法
修饰符 <TE, ...> 返回值类型 方法名(形参列表) {
    方法体...
}

泛型方法能使方法独立于类而产生变化,

Ps:

  • public与返回值中间非常重要,可以理解为声明此方法为泛型方法。
  • 只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
  • 表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
  • 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
栗子
/**
 *
 * @param t 传入泛型的参数
 * @param  泛型的类型
 * @return T 返回值为T类型
 */
public <T> T genercMethod(T t){
 System.out.println(t.getClass());
 System.out.println(t);
 return t;
}
泛型方法与可变参数

示例

 public <E> void print(E... e){
        for (E e1 : e) {
            System.out.println(e);
        }
 }

类型通配符

类型通配符一般是使用"?"代替具体的类型实参,当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。此时只能接受数据,不能往该集合中存储数据。

示例
//泛型通配符?:左边写 右边的泛型可以是任意类型
ArrayList<?> list1 = new ArrayList<Object>();
ArrayList<?> list2 = new ArrayList<String>();
ArrayList<?> list3 = new ArrayList<Integer>();

一般的,泛型通配符?主要应用在参数传递方面,如下:

public static void test(ArrayList<?> coll){

}

public static void main(String[] args) {
    // 此时,我们可以传递不同类似进去方法中去。
    ArrayList<Integer> list1 = new ArrayList<Integer>();
    test(list1);
    ArrayList<String> list2 = new ArrayList<String>();
    test(list2);
}

类型通配符

类型通配符的上限

语法

// 要求该泛型的类型,只能是实参类型,或实参类型的子类类型。/接口<? extends 实参类型>
类型通配符的下限
// 要求该泛型的类型,只能是实参类型,或实参类型的父类类型。/接口<? super 实参类型>
示例

现有:Animal Dog Cat类

class Animal{}//父类

class Dog extends Animal{}//子类

class Cat extends Animal{}//子类

泛型的上限示例

// 编译不通过
//        ArrayList list = new ArrayList();
 ArrayList<? extends Animal> list2 = new ArrayList<Animal>();
 ArrayList<? extends Animal> list3 = new ArrayList<Dog>();
 ArrayList<? extends Animal> list4 = new ArrayList<Cat>();
 
  

泛型的下限示例

ArrayList<? super Animal> list5 = new ArrayList<Object>();
ArrayList<? super Animal> list6 = new ArrayList<Animal>();
// 编译不通过
//        ArrayList list7 = new ArrayList();
// 编译不通过
//        ArrayList list8 = new ArrayList();

类型擦除

泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为–类型擦除。

无限制类型擦除

Java泛型_第1张图片

有限制类型擦除

Java泛型_第2张图片

擦除方法中类型定义的参数

Java泛型_第3张图片

桥接方法

Java泛型_第4张图片

泛型与数组

**泛型数组:**可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象,不过我们可以通过java.lang.reflect.Array的newInstance(Class,int)创建T[]数组。

 array = (T[])Array.newInstance(clz, length);

泛型与反射

反射常用的泛型类
Class<T>
Constructor<T>

示例

public static void main(String[] args) throws Exception {
         Class<Person> personClass = Person.class;
         Constructor<Person> constructor = personClass.getConstructor();
         Person person = constructor.newInstance();
     }

参考资料

https://www.bilibili.com/video/BV1xJ411n77R

你可能感兴趣的:(java,开发语言,jvm)