Java泛型(Generics)是JDK1.5 引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据。

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

泛型的优点 :

  • 将类型的检查从运行期提前到了编译期,保证数据类型的安全
  • 消除了强制类型转换的风险

泛型可以使用在类、接口、方法 、属性

1、泛型类

  1. 语法
    泛型类常用的泛型标识 : T 、E、 K、 V (其他大字母也可以的)

    1. class 类名 <泛型标识, 泛型标识 , ...> {
    2. private 泛型标识 变量名;
    3. .....
    4. }
  2. 泛型类使用语法
    JDK1.7之后 :

    1. 类名<具体数据类型> 对象名 = new 类名<具体的数据类型>();
    1. 类名<具体数据类型> 对象名 = new 类名<>();
  3. 泛型类使用注意事项

    • 泛型类如果没有指定具体的数据类型,则默认操作的是Object类型
    • 泛型的参数只能是引用类型,不能是基本数据类型
    • 泛型在逻辑上可以看成是多个不同的类型,但实际上都是相同的类型
  1. /**
  2. *
  3. * @author Administrator
  4. * T 和 E 的名称 可以随便起名 大小写都可以 ,但是泛型一般定义成大写
  5. * @param <T>
  6. * @param <E>
  7. */
  8. public class Point<T,E> {
  9. private T x;
  10. private E y;
  11. public T getX() {
  12. return x;
  13. }
  14. public void setX(T x) {
  15. this.x = x;
  16. }
  17. public E getY() {
  18. return y;
  19. }
  20. public void setY(E y) {
  21. this.y = y;
  22. }
  23. @Override
  24. public String toString() {
  25. return "Point [x=" + x + ", y=" + y + "]";
  26. }
  27. public Point(T x, E y) {
  28. super();
  29. this.x = x;
  30. this.y = y;
  31. }
  32. public Point() {
  33. super();
  34. // TODO Auto-generated constructor stub
  35. }
  36. }

测试类:

public class TestPoint {
    public static void main(String[] args) {
        //创建对象的时候 指定类型 new 对象时  可以不指定 (JDK1.7以上版本你可以不)
        Point<Integer,Integer> p1 =new Point<>(10,8);
        System.out.println(p1);

        Point<String,String> p2 = new Point<>("东经100","北纬20");
        System.out.println(p2);
        String x = p2.getX();

        Point<String, Integer> p3 = new Point<>("东经200",100);
        System.out.println(p3);
    }
}
/**
 * 定义统一返回值 
 * @author Administrator
 *
 * @param <T>
 */
public class ResponseResult<T> {
    private int code ;
    private String msg;
    private T data;

    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
    public ResponseResult(int code, String msg, T data) {
        super();
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    public ResponseResult() {
        super();
        // TODO Auto-generated constructor stub
    }
}
public class TestResult {
    public static void main(String[] args) {
        //返回用户数据
        ResponseResult<User> res1 = new ResponseResult<>(200, "查询成功", new User());
        //返回订单对象
        ResponseResult<Order> res2 = new ResponseResult<>(200,"查询订单成功",new Order());
    }
}

类上的泛型一般不超过两个

2、泛型接口

  1. 语法

    interface 接口名 <泛型标识, 泛型标识 , ...> {
    泛型标识 方法名(泛型标识 变量);
    .....
    }
    
  2. 泛型接口的使用

    • 实现类不是泛型类, 接口要明确数据类型
    • 实现类也是泛型类,实现类和接口的泛型要一致
  3. 案例
    指定类型的实现类 :
    不指定类型的实现类 (泛型类):

    /**
    * 操作数据库 增删改查
    * @author Administrator
    *
    */
    public interface IBaseDao<T> {
    void insert(T t);
    
    void update(T t);
    
    void deleteByKey(int id);
    
    T selectByKey(int id);
    
    T[] selectAll();
    }
    

    ```java /**

    • 实现类没有使用泛型 必须在接口上指定类型
    • @author Administrator / public class UserDaoImpl implements IBaseDao{

    @Override public void insert(User t) {

    // TODO Auto-generated method stub
    

    }

    @Override public void update(User t) {

    // TODO Auto-generated method stub
    

    }

    @Override public void deleteByKey(int id) {

    // TODO Auto-generated method stub
    

    }

    @Override public User selectByKey(int id) {

    // TODO Auto-generated method stub
    return null;
    

    }

    @Override public User[] selectAll() {

    // TODO Auto-generated method stub
    return null;
    

    }

}

```java
/**
 * 实现类的泛型 和 接口的泛型 必须一致
 * @author Administrator
 *
 * @param <T>
 */
public class BaseImpl<T> implements IBaseDao<T> {

    @Override
    public void insert(T t) {
        // TODO Auto-generated method stub
        System.out.println("插入数据成功..."+t);
    }

    @Override
    public void update(T t) {
        // TODO Auto-generated method stub

    }

    @Override
    public void deleteByKey(int id) {
        // TODO Auto-generated method stub

    }

    @Override
    public T selectByKey(int id) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public T[] selectAll() {
        // TODO Auto-generated method stub
        return null;
    }
}

3、泛型方法

首先我们做一个方法的区分,以前在泛型类中使用的方法并不是泛型方法,只是泛型类中的成员方法

泛型类 :在实例化类的时候指明泛型的具体类型

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

  1. 语法定义

    修饰符  <T, E , ...>  返回值类型 方法名(形参列表){
    方法体...
    }
    
  2. 泛型方法使用注意事项

    • 修饰符与返回值类型中间定义的<T,E...>就是在声明此方法为泛型方法
    • 只有声明了的方法才是泛型方法,泛型类中使用了泛型的方法不是泛型方法,而是类的成员方法
    • <T>表示该方法在定义时可以使用泛型T, 这时就可以在方法中使用泛型类型T了
    • 泛型方法能使方法独立于类的泛型单独使用
    • 如果static修饰的方法使用泛型,则该方法必须声明为泛型方法 在泛型类中声明了静态方法 ,必须声明成 泛型方法
public class Container<T> {

    // 成员方法: 使用了泛型做形参的类型, 泛型做方法的返回类型
    public  T getValue(T data) {
        return data;
    }

    // 泛型方法 :  在修饰符与方法的返回类型中间声明一个<泛型标识>
    // 泛型方法的泛型类型是在真正调用方法的时候确定的,与类的泛型无关
    public <E> E getKey(E key) {
        return key;
    }

    // 使用了泛型类若想声明为静态方法, 则该方法必须是泛型方法
    public static <E> E getStaticKey(E key) {
        return key;
    }
}

4、通配符

泛型通配符一般使用 ? 代替具体的类型参数。所以,类型通配符是类型实参,而不是类型形参

通配符使用的注意事项

  • 编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?

    public static <?> void test(ArrayList<?> list){}
    
  • 编译错误:不能用在泛型类的声明上

    public class CommonTypeClass<?>{ }
    
  • 编译错误:不能用在创建对象上,右边属于创建集合对象

    ArrayList<?> list2 = new ArrayList<?>();
    

只能用在声明类型(属性)、方法参数上,不能用在定义泛型类上

4.1 无边界通配符

无边界通配符 就是 ? :

public class MyResult {
    private String name ;
    private int code ;
    private List<?> list;   // 使用在属性上

    //省略 set 、get方法

    //使用在方法的参数上
    public void parseData(List<?> lists){
        for(Object o :lists){
            System.out.println(o);
        }
    }
}

4.2 上限通配符

语法 :

类/接口<? extends 实参类型>

限制数据的赋值 必须是 限制类及其子类 :

public class ResultData {
    private List<? extends Dog> lists ; //上限统配符

    public void setLists(List<? extends Dog> lists) {
        this.lists = lists;
    }
    public List<? extends Dog> getLists() {
        return lists;
    }
}
public class TestResultData {
    public static void main(String[] args) {
        /*
         * private List<? extends Dog> lists ; //上限统配符
         */
        ResultData data = new ResultData();
        //创建的一个 Taidi的集合 他是Dog子类
        List<Taidi> td = new ArrayList<>();
        td.add(new Taidi());
        td.add(new Taidi());

        data.setLists(td);
        //创建一个 Animal的集合 
        List<Animal> as = new ArrayList<>();
        as.add(new Animal());
        //data.setLists(as);  因为 ResultData中的lists 集合 限制了 集合中的数据 那必须是Dog及其子类
    }
}

4.3 、下限通配符

语法 :

类/接口<? super 实参类型>

限制数据赋值时 必须是 类型本身及其父类

public class ResultData {
    private List<? super Dog> lists ; //下限统配符

    public List<? super Dog> getLists() {
        return lists;
    }

    public void setLists(List<? super Dog> lists) {
        this.lists = lists;
    }
}
public class TestResultData {
    public static void main(String[] args) {
        /*
         * private List<? super Dog> lists ; //下限统配符
         */
        ResultData data = new ResultData();
        //创建的一个 Taidi的集合 他是Dog子类
        List<Taidi> td = new ArrayList<>();
        td.add(new Taidi());
        td.add(new Taidi());

        //data.setLists(td);  lists属性 限制的是 必须是 Dog 及其父类 
        //创建一个 Animal的集合 
        List<Animal> as = new ArrayList<>();
        as.add(new Animal());
        data.setLists(as);  
    }
}

5、泛型的擦除

泛型是在 Java编译的时候 ,根据泛型进行类型检查 ,把一些类型不匹配的错误 ,提前到编译期就呈现出来 ,但是一旦编译成了class文件,在class文件中不会有泛型 。

也就是说 泛型虽然多了检查类型这个功能,但是对程序运行的效率没有任何影响