Java - 泛型 1
泛型 1
使用泛型的原因
为了统计学生成绩,要求设计一个Score对象,包括课程名称、课程号、课程成绩,但是成绩分为两种,一种是以优秀、良好、合格 来作为结果,还有一种就是 60.0、75.5、92.5 这样的数字分数,可能高等数学这门课是以数字成绩进行结算,而计算机网络实验这门课是以等级进行结算,这两种分数类型都有可能出现,那么现在该如何去设计这样的一个 Score 类呢?
现在的问题就是,成绩可能是String类型,也可能是Integer类型,如何才能很好的去存可能出现的两种类型呢?
可以直接使用一个 Object 类进行存储成绩
public class Score {
String name;
String id;
Object value; //因为Object是所有类型的父类,因此既可以存放Integer也能存放String
public Score(String name, String id, Object value) {
this.name = name;
this.id = id;
this.score = value;
}
}
以上的方法虽然很好地解决了多种类型存储问题,但是Object类型在编译阶段并不具有良好的类型判断能力,很容易出现以下的情况:
public static void main(String[] args) {
Score score = new Score("数据结构与算法基础", "EP074512", "优秀"); //是String类型的
...
Integer number = (Integer) score.score; //获取成绩需要进行强制类型转换,虽然并不是一开始的类型,但是编译不会报错
}
使用Object类型作为引用,对于使用者来说,由于是 Object 类型,所以说并不能直接判断存储的类型到底是 String 还是 Integer ,取值只能进行强制类型转换,显然无法在编译期确定类型是否安全,项目中代码量非常之大,进行类型比较又会导致额外的开销和增加代码量,如果不经比较就很容易出现类型转换异常,代码的健壮性有所欠缺
所以说这种解决办法虽然可行,但并不是最好的方案。
为了解决以上问题,JDK 5新增了泛型,它能够在编译阶段就检查类型安全,大大提升开发效 率。
泛型类 ClassName<T>
泛型其实就一个待定类型,我们可以使用一个特殊的名字表示泛型,泛型在定义时并不明确是什么类型,而是需要到使用时才会确定对应的泛型类型。
我们可以将一个类定义为一个泛型类:
public class Score<T> {
//泛型类需要使用<>,我们需要在里面添加1 - N个类型变量
String name;
String id;
T value;
//T会根据使用时提供的类型自动变成对应类型
public Score(String name, String id, T value) {
//这里T可以是任何类型,但是一旦确定,那么就不能修改了
this.name = name;
this.id = id;
this.value = value;
}
}
具体使用:
public static void main(String[] args) {
Score<String> score = new Score<String>("计算机网络", "EP074512", "优秀");
// 因为现在有了类型变量,在使用时同样需要跟上<>并在其中填写明确要使用的类型
// 这样我们就可以根据不同的类型进行选择了
// 这种形式也可以
Score<String> score = new Score<>("计算机网络", "EP074512", "优秀");
String value = score.value;
//一旦类型明确,那么泛型就变成对应的类型了
System.out.println(value);
}
泛型将数据类型的确定控制在了编译阶段,在编写代码的时候就能明确泛型的类型,如果类型不符合,将无法通过编译!
因为是具体使用对象时才会明确具体类型,所以说静态方法中是不能用对象定义的泛型的
我们在方法中使用待确定类型的变量时,因为此时并不明确具体是什么类型,那么默认会认为这个变量是一个
Object类型的变量,因为无论具体类型是什么,一定是Object类的子类。不能通过这个不确定的类型变量就去直接创建对象和对应的数组.
具体类型不同的泛型类变量,不能使用不同的变量进行接收

如果要让某个变量支持引用确定了任意类型的泛型,那么可以使用
?通配符 就好像默认其是Object类型public static void main(String[] args) { Test<?> test = new Test<Integer>(); test = new Test<String>(); Object o = test.value; //但是注意,如果使用通配符,那么由于类型不确定,所以说具体类型同样会变成 Object }泛型变量不止可以只有一个,如果需要使用多个的话,我们也可以定义多个
那么在使用时,就需要将这三种类型都进行明确指定public class Test<A, B, C> { //多个类型变量使用逗号隔开 public A a; public B b; public C c; } ------------------------------------- public static void main(String[] args) { Test<String, Integer, Character> test = new Test<>(); //使用钻石运算符可以省略其中的类型 test.a = "lbwnb"; test.b = 10; test.c = '淦'; }泛型只能确定为一个引用类型,基本类型是不支持的, 如果要存放基本数据类型的值,我们只能使用对应的包装类
当然,如果是基本类型的数组,因为数组本身是引用类型,所以说是可以的
泛型和多态
不只是类,包括接口、抽象类,都是可以支持泛型的:
public interface Study<T> {
T test();
}
当子类实现此接口时,我们可以选择在实现类明确泛型类型,或是继续使用此泛型让具体创建的对象来确定类型:
public class Main {
public static void main(String[] args) {
A a = new A();
Integer i = a.test();
}
static class A implements Study<Integer> {
//在实现接口或是继承父类时,如果子类是一个普通类,那么可以直接明确对应类型
@Override
public Integer test() {
return null;
}
}
}
或者是继续摆烂,依然使用泛型:
public class Main {
public static void main(String[] args) {
A<String> a = new A<>();
String i = a.test();
}
static class A<T> implements Study<T> {
//让子类继续为一个泛型类,那么可以不用明确
@Override
public T test() {
return null;
}
}
}
继承也是同样的:
static class A<T> {
}
static class B extends A<String> {
}
