泛型

泛型是Java中的一种类型机制,它允许在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。泛型只在编译阶段有效,通过类型擦除,泛型信息不会进入运行时。文章介绍了泛型的使用,包括泛型类、泛型接口、泛型方法,以及类型通配符的运用,强调了其在避免类型转换异常和提高代码可读性方面的作用。

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

        泛型是jdk5引入的类型机制,将类型参数化。泛型机制将类型转换时的类型检查从运行时提前到了编译时,使用泛型编写的代码比杂乱的使用object并在需要时再强制类型转换的机制具有更好的可读性和安全性。在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

一、举例

    // ArrayList存放任意类型
    public static void main(String[] args) {
        List lists= new ArrayList<>();
        lists.add("AA");
        lists.add(111);
        for (Object object:lists) {
            System.out.println("测试结果:"+(String)object);
        }
    }

运行结果:

测试结果:aaaa
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

上面例子中分别添加了String类型和Integer类型,获取数据时都用String类型来获取,所以程序会出现类型错误的异常。为了能在编译器就解决类似这样的问题,所以需要使用泛型。

上面list重新初始化:
 List<String> lists= new ArrayList<String>();
 
 //在编译期就会报错
 lists.add(111); 

二、特性

        泛型只在编译阶段有效,在编译之后程序会采取去泛型化的措施也就是泛型擦除,泛型信息不会进入到运行时阶段。泛型类型在逻辑上可以看成是多个不同的类型,实际上都是相同的基本类型。也就是说jvm中没有泛型对象,只有普通对象。

    //擦除方式 定义部分,即尖括号中间的部分直接擦除。
    public class GenericClass<T extends Comparable>{}
    //擦除后:
    public class GenericClass{}

    public static void main(String[] args) {
        List<String> stringArrayList = new ArrayList<String>();
        List<Integer> integerArrayList = new ArrayList<Integer>();
        Class classStringArrayList = stringArrayList.getClass();
        Class classIntegerArrayList = integerArrayList.getClass();

        if(classStringArrayList.equals(classIntegerArrayList)){
            System.out.println("泛型测试类型相同");
        }
    }
    
    // 运行结果: 
    泛型测试类型相同

三、使用方式

使用方式分为泛型类、泛型接口、泛型方法、类型通配符。

1.泛型类

        用于类的定义中被称为泛型类。泛型类是在类名后面添加类型参数的声明,在实例化类的时候指明泛型的具体类型。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。

//类名称<泛型标识:随意写,常用的有T、E、K、V>
public class GenericVO<T> {
    //T为外部调用的时候定义的类型
    public T key;

    public T getKey() {
        return key;
    }

    public void setKey(T key) {
        this.key = key;
    }

    // 泛型类
    public static void main(String[] args) {
        GenericVO<Integer> genericVOInteger = new GenericVO<>();
        genericVOInteger.setKey(111);
        System.out.println("int类型取值:"+genericVOInteger.getKey());
        GenericVO<String> genericVOString = new GenericVO<>();
        genericVOString.setKey("QQQ");
        System.out.println("string类型取值:"+genericVOString.getKey());

        GenericVO genericVO1 = new GenericVO();
        genericVO1.setKey(11.44);
        System.out.println("1数据为:"+ genericVO1.getKey());
        GenericVO genericVO2 = new GenericVO();
        genericVO2.setKey("aaa");
        System.out.println("2数据为:"+ genericVO2.getKey());

        GenericVO<Double> genericVO3 = new GenericVO();
        genericVO3.setKey(11.5555);
        System.out.println("3数据为:"+ genericVO3.getKey());
    }
}

运行结果:

int类型取值:111
string类型取值:QQQ
1数据为:11.44
2数据为:aaa
3数据为:11.5555

2.泛型接口

        泛型接口与泛型类的使用类似。

/**
 * 定义一个泛型接口
 * @param <T>
 */
public interface GeneratorService<T>{
    T test();
}
/**
 * 实现泛型的类没有传入实参时,在声明类的时候把泛型声明一起加到类中。
 * @param <T>
 */
public class GeneratorServiceImpl<T> implements GeneratorService<T> {
    @Override
    public T test() {
        return null;
    }
}
/**
 * 实现类传入泛型实参字符串类型
 */
public class GeneratorStringServiceImpl implements GeneratorService<String>{
    @Override
    public String test() {
        return "hello";
    }
}

3.类型通配符

        类型通配符一般使用 ? 代表的是具体的类型实参。如果定义的类型不确定时可以使用 ? 。

    public static void main(String[] args) {
        GenericVO<Integer> genericVOInteger = new GenericVO<>();
        getKeyValue(genericVOInteger);
    }
    //定义泛型通配符 ?
    private static void getKeyValue(GenericVO<?> obj){
        System.out.println("key value:"+ obj.getKey());
    }

4.泛型方法

泛型方法,在调用方法的时候指明泛型的具体类型。 泛型方法定义规则:

  • 1.所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),声明部分在方法返回类型之前。
  • 2.参数声明部分多个类型参数,使用逗号隔开。一个泛型参数(一个类型变量),是用于指定一个泛型类型名称的标识符。
  • 3.类型参数可用来声明返回值类型。
  • 4.泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型(包装类),不能是原始类型(int,double,char等)。

4.1.泛型方法的基本用法

/**
 * 简单的泛型方法示例
 */
public class GenericMethodTest {
    /**
     * 泛型方法 printArray
     * @param genericVO 传入的泛型实参
     * @param <T>  返回值为T类型
     */
    public static <T> T getKey(GenericVO<T> genericVO) {
        // 输出数组元素
        System.out.println("输出key value:"+ genericVO.getKey());
        T test = genericVO.getKey();
        return test;
    }

    /**
     * 普通的方法,使用了泛型类做参数
     * @param genericVO 传入的泛型实参
     */
    public static void getKeyMethod(GenericVO<String> genericVO) {
        // 输出数组元素
        System.out.println("输出key value:"+ genericVO.getKey());
    }

    public static void main(String args[]) {
        GenericVO genericVO = new GenericVO();
        genericVO.setKey("hello");
        getKey(genericVO);
        getKeyMethod(genericVO);
    }
}

4.2.泛型类中的泛型方法

        泛型方法可以出现杂任何地方和任何场景中使用。泛型类中的泛型方法属于一种特殊情况。

/**
 * 泛型类中的泛型方法
 */
public class GenericHello {

    static class GenericVO {
        @Override
        public String toString() {
            return "aa";
        }
    }

    static class Aa extends GenericVO {
        @Override
        public String toString() {
            return "bb";
        }
    }

    static class Bb {
        @Override
        public String toString() {
            return "cc";
        }
    }

    static class GenerateTest<T> {
        public void show_1(T t) {
            System.out.println(t.toString());
        }

        //泛型类中声明泛型方法,泛型T可以任意类型
        public <T> void show_2(T t) {
            System.out.println(t.toString());
        }

        //泛型类中声明泛型方法,泛型E可以任意类型
        //泛型类中没有声明泛型<E>,由于泛型方法在声明的时候会声明泛型,编译器可以正确识别泛型方法中识别的泛型.
        public <E> void show_3(E t) {
            System.out.println(t.toString());
        }
    }

    public static void main(String[] args) {
        Aa aa = new Aa();
        Bb bb = new Bb();
        GenerateTest<GenericVO> generateTest = new GenerateTest<>();
        //aa是GenericVO的子类
        generateTest.show_1(aa);
        //编译器会报错
        //generateTest.show_1(bb);

        generateTest.show_2(aa);
        generateTest.show_2(bb);

        generateTest.show_3(aa);
        generateTest.show_3(bb);
    }
}

4.3.泛型方法与可变参数

    //泛型方法与可变参数
    public static <T> void printKeyValue(T... args){
        System.out.println("泛型方法与可变参数key value:");
        for(T t : args){
            System.out.print(t+" ");
        }
    }
    //演示结果:
    泛型方法与可变参数key value:
    111 QQQ aaa 11.5555 

4.4.有界的参数类型

        使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制。要声明一个有界的类型参数,列出类型参数的名称,后跟extends关键字,后面跟着它的上界。

/**
 * 泛型方法 有界的参数类型
 */
public class MaximumTest {
    // 比较三个值并返回最大值
    //<T extends Comparable>,表示的是Comparable及其子类型。
    public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
        // 假设x是初始最大值
        T max = x;
        if (y.compareTo(max) > 0) {
            //y 更大
            max = y;
        }
        if (z.compareTo(max) > 0) {
            // 现在 z 更大
            max = z;
        }
        // 返回最大对象
        return max;
    }

    public static void main(String args[]) {
        //以下类型都属于Comparable的实现类
        //Integer
        System.out.printf("%d, %d 和 %d 中最大的数为 %d\n", 3, 4, 5, maximum(3, 4, 5));
        // Float
        System.out.printf("%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n", 6.6, 8.8, 7.7, maximum(6.6, 8.8, 7.7));
    }
}

4.5.泛型与继承

继承的原则:继承泛型类时,必须对父类中的类型参数进行初始化。或者说父类中的泛型参数必须在子类中可以确定具体类型。

初始化方式:

  • 具体类型初始化
/**
 * 继承泛型类
 */
public class Son extends GenericVO<String>{
}
  • 用子类中的泛型类型初始化父类
public class Son<T> extends GenericVO<T>{
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值