【Java】泛型

本文详细介绍了Java泛型的概念、用途,包括为何引入泛型、泛型在集合中的应用、如何自定义泛型类和接口、泛型方法以及在继承中的体现。通过实例展示了泛型提高代码安全性、避免类型转换的优势,并探讨了通配符的使用及其限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

泛型

为什么要有泛型

集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList 这个就是类型参数,即泛型。

  1. 解决元素存储的安全性问题
  2. 解决获取数据元素时,需要类型强制转换的问题

没有泛型时:

在这里插入图片描述
有泛型时:
在这里插入图片描述

Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。

在集合中使用泛型

ArrayList<Integer> list = new ArrayList<>();//类型推断
list.add(78);
list.add(88);
list.add(77);
list.add(66);
//遍历方式一:
//for(Integer i : list){
//不需要强转
//System.out.println(i);
//}
//遍历方式二:
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}

泛型的嵌套:

Map<String,Integer> map = new HashMap<String,Integer>();
map.put("Tom1",34);
map.put("Tom2",44);
map.put("Tom3",33);
map.put("Tom4",32);
//添加失败
//map.put(33, "Tom");
Set<Entry<String,Integer>> entrySet = map.entrySet();
Iterator<Entry<String,Integer>> iterator = entrySet.iterator();
while(iterator.hasNext()){
Entry<String,Integer> entry = iterator.next();
System.out.println(entry.getKey() + "--->" + entry.getValue());
}

泛型在接口的使用:

public class Employee implements Comparable<Employee>

使用泛型后,该接口内涉及泛型的方法的参数都会变成该类型

总结:

  1. 集合接口或集合类在jdk5.0时都修改为带泛型的结构。

  2. 实例化集合类时,可以指明具体的泛型类型

  3. 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。

  4. 比如:add(E e) —>实例化以后:add(Integer e)

  5. 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换

  6. 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。

  7. jdk7的新特性:

    Map<String,Integer> map = new HashMap();//HashMap<String,Integer>();
    

    前面用了泛型后面可以不用。

如何自定义泛型结构

interface List 和 class GenTest<K,V> ,其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写。

自定义泛型类、接口

public class Order<T> {

    String orderName;
    int orderId;

    //类的内部结构就可以使用类的泛型

    T orderT;

    public Order(){

    }

    public Order(String orderName,int orderId,T orderT){
        this.orderName = orderName;
        this.orderId = orderId;
        this.orderT = orderT;
    }

    //如下的三个方法都不是泛型方法
    public T getOrderT(){
        return orderT;
    }

    public void setOrderT(T orderT){
        this.orderT = orderT;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderName='" + orderName + '\'' +
                ", orderId=" + orderId +
                ", orderT=" + orderT +
                '}';
    }
}

如果定义了泛型类,实例化没有指明类的泛型,则以此泛型类型为Object类型处理,但不完全等价于Object类型,要么一直用泛型,要么都不用。

    Order order = new Order();
    order.setOrderT(123);
    order.setOrderT("ABC");

由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。

    SubOrder sub1 = new SubOrder();
    sub1.setOrderT(1122);
    SubOrder1<String> sub2 = new SubOrder1<>();
    sub2.setOrderT("order2...");

泛型不同的引用不能相互赋值

        ArrayList<String> list1 = null;
        ArrayList<Integer> list2 = new ArrayList<Integer>();
        list1 = list2;

如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象,泛型的指定中不能使用基本数据类型,可以使用包装类替换。

异常类不能是泛型的,自定义的异常类也不可以。

    public void show(){
        //编译不通过
        try{


       }catch(T t){

        }

    }

静态方法中不能使用类的泛型,报错,因为泛型是在实例化对象才确定的,但是static方法是在类加载的时候就可以使用的

public static void show(T orderT){
	System.out.println(orderT);
    }

泛型类数组的创建

        //编译不通过
        T[] arr = new T[10];
        //编译通过
        T[] arr = (T[]) new Object[10];

父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:

意思就是子类可以继承父类所有的,然后自己可以额外增加。

class Father<T1, T2> {
}

子类不保留父类的泛型:按需实现

没有类型 擦除

class Son1 extends Father {// 等价于class Son extends Father<Object,Object>{
}

class Son<A, B> extends Father{//等价于class Son extends Father<Object,Object>{
}

具体类型

class Son2 extends Father<Integer, String> {
}

class Son2<A, B> extends Father<Integer, String> {
}

子类保留父类的泛型:泛型子类

全部保留

class Son3<T1, T2> extends Father<T1, T2> {
}

class Son3<T1, T2, A, B> extends Father<T1, T2> {
}

部分保留

class Son4<T2> extends Father<Integer, T2> {
}

class Son4<T2, A, B> extends Father<Integer, T2> {
}

泛型方法

泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。换句话说,泛型方法所属的类是不是泛型类都没有关系。泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。

 public static <E>  List<E> copyFromArrayToList(E[] arr){

        ArrayList<E> list = new ArrayList<>();

        for(E e : arr){
            list.add(e);
        }
        return list;

    }
 
 @Test
    public void test4(){
        Integer[] arr = new Integer[]{1,2,3,4};
        //泛型方法在调用时,指明泛型参数的类型。
        List<Integer> list = order.copyFromArrayToList(arr);

        System.out.println(list);
    }
}

泛型在继承方面的体现

如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G并不是G的子类型!两者是并列关系。

比如:String是Object的子类,但是List并不是List的子类。

补充:类A是类B的父类,A 是 B 的父类

通配符的使用

通配符:?

类A是类B的父类,G和G是没有关系的,二者共同的父类是:G<?>

public void print(List<?> list){
    Iterator<?> iterator = list.iterator();
    while(iterator.hasNext()){
        Object obj = iterator.next();
        System.out.println(obj);
    }

对于List<?>就不能向其内部添加数据,例外:可以添加null

因为?就代表不知道是什么,那么不知道什么就无法添加数据。

允许读取数据,读取的数据类型为Object

    Object o = list.get(0);
    System.out.println(o);

有限制条件的通配符的使用

    ? extends A:
            G<? extends A> 可以作为G<A>G<B>的父类,其中BA的子类 理解为G<?>小于等于A

    ? super A:
            G<? super A> 可以作为G<A>G<B>的父类,其中BA的父类 理解为G<?>大于等于A
        List<? extends Person> list1 = null;
        List<? super Person> list2 = null;

        List<Student> list3 = new ArrayList<Student>();
        List<Person> list4 = new ArrayList<Person>();
        List<Object> list5 = new ArrayList<Object>();

        list1 = list3;
        list1 = list4;
//报错        list1 = list5;

//报错        list2 = list3;
        list2 = list4;
        list2 = list5;

读取数据

        //读取数据:
        list1 = list3;
        Person p = list1.get(0);
        //编译不通过
        //Student s = list1.get(0);

        list2 = list4;
        Object obj = list2.get(0);
        编译不通过
//        Person obj = list2.get(0);

写入数据

可以写入子类或相同类的数据

        //写入数据:
        //编译不通过
//        list1.add(new Student());无法确定list是什么类,及extends的不能写入

        //编译通过 及super的可以写入小于等于该类的
        list2.add(new Person());
        list2.add(new Student());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值