走进泛型
Java 泛型是 JDK 5 引入的特性,它提供了编译时类型安全检测机制。通过泛型,我们可以在定义类、接口或方法时使用类型参数,这些参数在使用时会被具体类型替换。
泛型程序设计
为了统计学生成绩,要求设计一个Score对象,包括课程名称、课程号、课程成绩,但是成绩分为两种,一种是以优秀、良好、合格 来作为结果,还有一种就是60.0、75.5、92.5这样的数字分数,可能高等数学这门课是以数字成绩进行结算,而计算机网络实验这门课是以等级进行结算,这两种分数类型都有可能出现,那么现在该如何去设计这样的一个Score类呢?
现在的问题就是,成绩可能是String类型,也可能是Integer类型,如何才能很好的去存可能出现的两种类型呢?

public class Score {
    String name;
    String id;
    Object grade;//因为Object是所有类型的父类,因此既可以存放Integer也能存放String

    public Score(String name,String id,Object grade){
        this.name=name;
        this.id=id;
        this.grade=grade;
    }
    public Object getGrade() {
        return grade;
    }
}

虽然用object解决了多种类型存储问题,但是使用Object类型作为引用,对于使用者来说,由于是Object类型,所以说并不能直接判断存储的类型到底是String还是Integer,取值只能进行强制类型转换,显然无法在编译期确定类型是否安全,项目中代码量非常之大,进行类型比较又会导致额外的开销和增加代码量,如果不经比较就很容易出现类型转换异常,代码的健壮性有所欠缺。
所以说这种解决办法虽然可行,但并不是最好的方案。

泛型类
泛型其实就是一个待定类型,我们可以使用一个特殊的名字表示泛型,泛型在定义时并不明确是什么类型,而是需要到使用时才会确定对泛型类型。
定义一个泛型类:

public class Score<T>{//泛型类需要使用<>
    String name;
    String id;
    T grade;//T会根据使用时提供的类型自动变成对应类型

    public Score(String name,String id,T grade){//这里的T可以是任何类型,一旦确认了就不可以更改了
        this.name=name;
        this.id=id;
        this.grade=grade;
    }
    public T getGrade() {
        return grade;
    }
}

这样就可以不用强制转换了

public class Main {
    public static void main(String[] args){
        Score<String> score=new Score<>("课程","Ep02","优秀");
        //因为现在有了类型变量,在使用时同样需要跟上<>并在其中填写明确要使用的类型
        //这样我们就可以根据不同的类型进行选择了
        String grade= score.grade;//一旦类型明确了,那么泛型就变成对应的类型了
        System.out.println(grade);
    }
}

泛型只能确定为一个引用类型,基本类型是不支持的。当然,如果是基本类型的数组,因为数组本身是引用类型,所以说是可以的。

泛型与多态

// 主类,程序执行的入口点
public class Main {
    public static void main(String[] args) {
        // 创建泛型类A的实例,指定类型参数为String
        // 注意:此处存在钻石操作符的省略写法,完整写法应为 new A<String>()
        A<String> a = new A();
        // 调用实例方法test,返回值为String类型(此处返回null)
        a.test();
    }

    // 静态泛型类A,实现了Test接口
    // 类型参数T在类实例化时确定
    static class A<T> implements Test<T> {
        // 实现接口方法test,返回类型为T
        // 注意:当前实现返回null,实际应用中应返回具体对象
        @Override
        public T test() {
            return null;
        }
    }
}

// 泛型接口Test,定义了一个返回类型为E的抽象方法
// 类型参数E由实现类指定
public interface Test<E> {
    E test();
}

接口方法返回类型也可以为String,修改一下接口方法static class A implement Tsst{

@Override
    public String test() {
        return null;
    }
}

即可
继承也是同样的

    public class Main {
    public static void main(String[] args) {
        // 创建泛型子类的实例,指定类型参数为String
        StringHandler handler = new StringHandler();
        // 调用继承的泛型方法
        String result = handler.process();
    }
}

// 泛型抽象基类,定义通用行为
abstract class DataHandler<T> {
    // 泛型抽象方法,由子类实现具体逻辑
    public abstract T process();
    
    // 通用方法,可操作泛型类型T
    protected void printType(T data) {
        System.out.println("Type: " + data.getClass().getName());
    }
}

// 具体子类,继承泛型基类并指定类型参数
class StringHandler extends DataHandler<String> {
    @Override
    public String process() {
        // 实际应用中应返回具体对象,这里保持与原代码一致返回null
        return null;
    }
}

// 另一个具体子类示例,处理Integer类型
class IntegerHandler extends DataHandler<Integer> {
    @Override
    public Integer process() {
        return 42; // 返回Integer类型的结果
    }
}   

泛型方法
类型变量并不是只能在泛型类中才可以使用,我们也可以定义泛型方法。

    public class Main {
    public static void main(String[] args){
        Integer s=test(10);
    }
    public static <T> T test(T t){
        return t;
    }
}

不是静态也行

    public class Main {
    public static void main(String[] args){
        Main main=new Main();
        Integer s=main.test(10);
        System.out.println(s);
    }
    public <T> T test(T t){
        return t;
    }
}

泛型界限
泛型界限用于限制类型参数的范围,确保类型参数满足特定条件。
分类:
上界通配符:<? extends 类型> - 允许传入该类型或其子类型
下界通配符:<? super 类型> - 允许传入该类型或其父类型
无界通配符:<?> - 允许任何类型

    // 上界泛型类:通过<T extends Number>限制类型参数T必须是Number类或其子类
public class Container<T extends Number> {
    // 存储泛型类型T的实例,由于T是Number的子类,必然包含数值相关特性
    private T value;
    
    // 方法返回value的double值
    // 因为T继承自Number,而Number类有doubleValue()方法,所以这里可以安全调用
    public double getDoubleValue() { 
        return value.doubleValue(); 
    }
}

// 上界通配符方法:参数使用<? extends Number>,表示传入的List元素必须是Number或其子类
// 这种写法的核心作用是"安全读取"(只能从集合中取数据,不能添加数据)
public static double sumAll(List<? extends Number> list) {
    double sum = 0;
    // 遍历集合时,所有元素都可以被当作Number处理(因为它们都是Number的子类)
    for (Number num : list) {
        // 调用Number的doubleValue()方法累加,无论实际是Integer还是Double都能兼容
        sum += num.doubleValue();
    }
    return sum;
}

// 下界通配符方法:参数使用<? super Integer>,表示传入的List元素必须是Integer或其父类
// (如Number、Object),核心作用是"安全写入"(只能向集合中添加Integer及其子类)
public static void addNumbers(List<? super Integer> list) {
    // 循环添加1到10的整数(自动装箱为Integer)
    for (int i = 1; i <= 10; i++) {
        // 由于list的元素类型是Integer或其父类,添加Integer是安全的
        // 例如:List<Number>、List<Object>都能接收Integer
        list.add(i);
    }
}

类型擦除机制
Java 泛型是通过类型擦除实现的,这意味着:
编译后的字节码中不包含泛型类型信息
类型参数被替换为其边界类型(无界时为 Object)
桥接方法用于维护多态性

// 编译前
Box<String> stringBox = new Box<>();
Box<Integer> intBox = new Box<>();

// 编译后(类型擦除)
Box stringBox = new Box();
Box intBox = new Box();
分类: Java 标签: 泛型

评论

暂无评论数据

暂无评论数据

目录