泛型(Generic)

本文介绍了Java中的泛型概念,从泛型的引入、在集合中的使用,到自定义泛型类和接口,以及泛型方法和通配符的运用。通过示例展示了如何提高代码的类型安全性和减少类型转换,同时也讨论了泛型在继承和方法签名中的影响。

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

泛型(Generic)

1. 泛型概述

所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值参数类型。这个类型参数将在使用时(例如, 继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。

jdk1.5引入泛型

2. 集合中使用泛型

实例化集合类时,“<>”指明具体的泛型类型。

泛型的类型必须是类,不能是基本数据类型;默认情况是java.lang.Object类。

package com.GenericTest.java;

import org.junit.Test;

import java.util.*;

/**
 * @author Wkm
 * @date 2023-02-02 上午 11:14
 * @description: 在集合中使用泛型
 */
public class GenericTest {
    //1.ArrayList举例
    @Test
    public void test1(){
        ArrayList<Integer> integers = new ArrayList<Integer>();
        //指明了Integer,再add就会进行类型检查
        integers.add(123);
        integers.add(45);
        integers.add(6);
        integers.add(789);

        //遍历,方法1:
        for(Integer num : integers){
            //此时不用再进行强转
            int temp = num;
            System.out.println(temp);
        }
    }

    //2.HashMap
    @Test
    public void test2(){
        Map<String, Integer> map = new HashMap<String, Integer>();
        //jdk7类型推断:省略new Xxx后的<>中的内容
        //Map<String, Integer> map = new HashMap<>();
        map.put("wkm", 85);
        map.put("yyy", 98);
        map.put("mm", 99);
        //泛型的嵌套:
        Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = entrySet.iterator();
        while (iterator.hasNext()){
            Map.Entry<String, Integer> next = iterator.next();
            String name = next.getKey();
            Integer score = next.getValue();
            System.out.println(name + " get score: " + score);
            /*
                mm get score: 99
                wkm get score: 85
                yyy get score: 98
             */
        }
    }
}

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

指明后,在集合类或接口凡是定义类和接口时,内部结构使用到类的泛型的位置都要指明泛型的类型,如:add(E e)实例化后为add(Integer e);

3. 自定义泛型结构

泛型类/接口

首先定义一个自定义泛型类:

package com.GenericTest.java;

/**
 * @author Wkm
 * @date 2023-02-02 下午 12:12
 * @description: 自定义泛型类(自定义泛型接口同理,区别就是类和接口的区别)
 */
public class CustomizingGenericTest<T> {
    private String name;
    private int age;
    private T order;

    public CustomizingGenericTest(){}
    public CustomizingGenericTest(String name, int age, T order){
        this.name = name;
        this.age = age;
        this.order = order;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public T getOrder() {
        return order;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setOrder(T order) {
        this.order = order;
    }

    @Override
    public String toString() {
        return "CustomizingGenericTest{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", order=" + order +
                '}';
    }
}

子类的定义:

package com.GenericTest.java;

/**
 * @author Wkm
 * @date 2023-02-19 下午 11:03
 * @description: TODO
 */
public class SubOrder extends CustomizingGenericTest<Integer>{
}
package com.GenericTest.java;

/**
 * @author Wkm
 * @date 2023-02-19 下午 11:06
 * @description: 此时SubOrder2还是泛型类
 */
public class SubOrder2<T> extends CustomizingGenericTest<T>{

}

测试代码:

package com.GenericTest.java;

import org.junit.Test;

/**
 * @author Wkm
 * @date 2023-02-19 下午 10:47
 * @description: 自定义泛型类的三种情况的说明
 */
public class GenericTest1 {
    @Test
    public void test(){
        //CustomizingGenericTest order = new CustomizingGenericTest();
        //order.setOrder(123);
        //order.setOrder("ABC");
        /*情况1:
        如果定义了泛型类,但实例化没有指明类的泛型,则此泛型类型位Object类型
        因此:定义了泛型类,就要在实例化时指明类的泛型
         */

        CustomizingGenericTest<String> order= new CustomizingGenericTest<String>("orderAA", 12, "order:AA");
        order.setOrder("this is orderAA");

    }

    @Test
    public void test2(){
        /*
        情况2:子类(SubOrder)继承父类(CustomizingGenericTest)时指明了泛型的类型,子类就是一个普通类
         */
        SubOrder subOrder = new SubOrder();
        subOrder.setOrder(1122);

        /*
        情况3:子类(SubOrder2)继承父类(CustomizingGenericTest)时未指明泛型的类型,子类仍是泛型类
         */
        SubOrder2<String> sub2 = new SubOrder2<>();
        sub2.setOrder("sub2");
    }
}

  1. 泛型类有多个时就定义多个(逗号隔开),如:<E1, E2, E3>
  2. 泛型类的构造器不要加<E>
  3. 泛型不同的引用不能相互赋值,如:ArrayList<String> list1 与 ArrayList<Integer> list2 不能相互赋值;
  4. 静态方法中不能使用泛型;
  5. 异常类不能声明为泛型;
  6. 不能使用 new E[], 可以使用强转:E[] elements = (E[]) new Object[capacity]; (参考ArrayList源码中的声明:Object[] elementData,而非泛型参数类型数组)

更为复杂的例子:

image-20230219233155754

image-20230219233309806

泛型方法

在上面的主类CustomizingGenericTest增加泛型方法

/*
    泛型方法:在方法中出现了泛型结构。
            泛型参数与所在的类的泛型无关。即泛型方法所属的类是不是泛型类均可。
     */
    public <E> List<E> copyFromArrayList(E[] arr){

        ArrayList<E> list = new ArrayList<>();
        for(E tempItem : arr){
            list.add(tempItem);
        }
        return list;
    }

测试:

@Test
    public void test3(){
        //泛型方法测试
        CustomizingGenericTest<String> order = new CustomizingGenericTest<>();
        Integer arr[] = new Integer[]{1, 2, 3, 4};
        //泛型方法调用时指明泛型类型(Integer),这个泛型与类的泛型(String)无关
        List<Integer> integers = order.copyFromArrayList(arr);
        System.out.println(integers);//[1, 2, 3, 4]
    }

4. 泛型在继承上的体现

  1. 类A是类B的父类,此时 G<A> 和 G<B> 是并列关系

    List<Object> list1 = null;
    List<String> list2 = null;
    //list1 = list2 //此时的list1和list2不具有子父类关系
    
    show1(list1);
    show2(list2);
    //每个G的A都要有自己的的方法
    public void show1(List<Object> list){
        //...
    }
    
    public void show2(List<String> list){
        //...
    }
    
  2. 类A是类B的父类,此时 A<G> 和 B<G> 是子父类关系

    List<String> list1 = null;
    list1 = new ArrayList<>;
    

5. 通配符:?

? 表示能通用适配一些泛型,不是确定的某一种。

public class GenericTest {

    @Test
    public void test(){
        //解决由于 G<A> 和  G<B> 是并列关系所带来的麻烦
        List<Object> list1 = null;
        List<String> list2 = null;
        
        //此时二者的公共父类就是G<?>
        List<?> list = list1;
        list = list2;
        //此时只定义一个print方法,编译通过
        print(list1);
        print(list1);
    }
    
    public void print(List<?> list){
        Iterator<?> iterator = list.iterator();
        while (iterator.hasNext()){
            //此时得用Object接收next()返回值
            Object next = iterator.next();
            System.out.println(next);
        }
    }
}

关于通过通配符操作list中的数据:可读但不可写

@Test
public void test2(){
    List<?> list1 = null;

    List<String> list = new ArrayList<>();
    list.add("AA");
    list.add("BB");
    list.add("CC");

    //不允许写数据(除了null)
    list1 = list;
    //list1.add("STR"); 编译不通过

    //允许读, 类型为Object
    Object o = list1.get(0);
}

有限制条件的通配符

 /*
    ? extends A: 可以匹配A或A的子类
    ? super A: 匹配A或A的父类
     */
    @Test
    public void test3(){
        List<? extends Person> list1 = null;
        List<? super Person> list2 = null;

        List<Person> list3 = null;
        List<Object> list4 = null;
        List<Student> list5 = null;

        list1 = list3;
        //list1 = list4; 编译不通过
        list1 = list5;

        list2 = list3;
        list2 = list4;
        //list2 = list5; 编译不通过

        //关于读数据:
        //此时用list1返回的数据类型要用Person接收;
        //list2返回的数据类型要用Object接收;

        //写数据
        //1. 编译不通过:此时接收的可能是Person的某个子类
        //list1.add(new Person());

        //2. 接收的是Person本身或者其父类,因此添加Person本身或其子类是可以的(多态)
        list2.add(new Person());
        list2.add(new Student());
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值