Java泛型

本文深入探讨Java泛型的概念及其在集合类中的应用,阐述了泛型如何帮助编译器在类型安全方面发挥作用,减少强制类型转换带来的风险,并通过实例展示了如何使用泛型接口、类和方法,以及如何设定类型通配符的上限,进一步提升代码的健壮性和可读性。

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

Why

JDK1.5增加泛型支持在很大程度上是为了让集合能记住其元素的数据类型。在没有泛型前,一旦把一个对象“丢进”java集合中,集合就会忘记对象的类型,把所有对象当成Object类型处理。当程序从集合中取出对象后,就需要进行强制类型转换,这种强制类型转换不仅使代码臃肿,而且容易引起ClassCastException异常。

增加了泛型支持后的集合的优点:

  • 可以记住集合中元素的类型,并可以在编译时检查集合中元素的类型,如果试图向集合中添加不满足类型要求的对象,编译器就会提示错误;
  • 可以让代码更加简洁,程序更加健壮;
  • 除此之外,Java泛型还增强了枚举类、反射等方面的功能。

    总结起来就是在编译时会检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码重用率。

import java.util.List;
import java.util.ArrayList;

public class Solution {
    public static void main(String[] args) {
        //创建一个只想保存字符串的List集合
        List strList = new ArrayList();

        strList.add("疯狂java讲义");
        strList.add("疯狂Android讲义"); 
        //“不小心”把一个Integer对象“丢进”了集合  
        strList.add(5);    //①

        strList.forEach(str->System.out.println(((String)str).length()));  //②
    }
}

上述程序②处引发ClassCastException异常


What、When、Where

所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类泛型接口泛型方法
Java引入泛型的好处是安全简单。

规则限制

  • 泛型的类型参数只能是类类型(包括自定义类),不能是简单类型;
  • 同一种泛型可以对应多个版本(因为参数类型是不确定的),不同的版本的泛型类实例是不兼容的;
  • 泛型的类型参数可以有多个;
  • 泛型的参数类型可以使用extends语句,例如< T extends superclass>。习惯上称为“有界类型”;
  • 泛型的参数类型还可以是通配符类型。例如Class< ? > classType = Class.forName(“java.lang.String”).

How

1、定义泛型接口、类

public class Apple<T> {
    private T info;
    public Apple(){}

    public Apple(T info) {
        this.info = info;
    }
    public void setInfo(T info) {
        this.info = info;
    }
    public T getInfo() {
        return this.info;
    }

    public static void main(String[] args) {
        Apple<String> a1 = new Apple<>("苹果");
        System.out.println(a1.getInfo());
        Apple<Double> a2 = new Apple<>(5.67); 
        System.out.println(a2.getInfo());
    }
}

注意:当创建泛型类声明自定义类,为该类定义构造函数时,构造函数名还是原来的类名,不要增加泛型声明。例如,为Apple< T>类定义构造函数,其构造函数名依然是Apple,而不是Apple< T>!调用该构造函数时却可以使用Apple< T>的形式,当然应该为T形参传入实际的类型参数。Java7提供了菱形语法,允许省略< >中的类型参数。

事实上,并不存在泛型类,例如,

import java.util.List;
import java.util.ArrayList;

public class Test {

    public static void main(String[] args) {

        List<String> l1 = new ArrayList<>();
        List<Integer> l2 = new ArrayList<>();
        System.out.println(l1.getClass()==l2.getClass());
    }
}

结果为true,因为不管泛型的实际类型参数是什么,他们在运行时总有同样的类(class)处理,在内存中也只占用一块内存空间,因此在静态方法、静态初始化块或者静态变量声明和初始化中不允许使用类型形参。比如下面代码:

public class R<T> {
    //下面代码错误,不能在静态变量声明中使用类型参数
    static T info;
    T age;
    public void foo(T msg){}
    //下面代码错误,不能在静态方法声明中使用类型参数
    public static void bar(T msg){}
}

由于系统并不会真正生成泛型类,所以intanceof运算符后不能使用泛型类。例如,下面代码是错误的:

java.util.Collection<String> cs = new java.util.ArrayList<>();
//下面代码编译时引起错误
if(cs intanceof java.util.ArrayList<String>(){}

2、设定类型通配符的上限

//定义一个抽象类Shape
public abstract class Shape {
    public abstract void draw(Canvas c);
}
//定义一个Shape的子类Circle
public class Circle extends Shape {
    public void draw(Canvas c) {
        System.out.println("在画布"+c+"上画一个圆");
    }
}
//定义一个Shape的子类Rectangle
public class Rectangle extends Shape {
    public void draw(Canvas c) {
        System.out.println("在画布"+c+"上画一个矩形");
    }
}
public class Canvas {
    public void drawAll(List<? extends Shape> shapes) {
        for(Shape s : shapes) {
            s.draw(this);
        }
    }
}

List< ? extends Shape > 是受限制通配符的例子,此处的问号(?)代表一个未知的类型,但是此处的这个未知类型一定是Shape的子类型,因此可以把Shape称为这个通配符的上限。

3、定义泛型方法
泛型方法的语法格式如下:

修饰符 <T,S> 返回值类型 方法名(形参列表){
     //方法体
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值