1. 泛型的优点
在没有泛型的情况的下,通过对类型 Object 的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是本身就是一个安全隐患。那么泛型的好处就是在编译的时候能够检查类型安全,并且所有的强制转换都是自动和隐式的。
使用泛型的例子:使用泛型指定数据类型和强制转换的比较,如下代码
public class Generic<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
public class GenericLearn {
// 不指定类型
public void noSpecifyType() {
Generic generic = new Generic();
generic.set("test");
// 需要使用强制类型转换
String test = (String) generic.get();
System.out.println(test);
}
// 这里使用泛型指定类型,指定类型为String
public void specifyType() {
Generic<String> generic = new Generic<>();
generic.set("test");
// 不需要使用强制类型转换
String test = generic.get();
System.out.println(test);
}
public static void main(String[] args) {
}
}
2. 使用泛型
1. 在这里我们使用java 的可变长数组ArrayList作为例子说明泛型。
使用ArrayList时,如果不定义泛型类型时,泛型类型实际上就是Object,此时,只能把<T>当作Object使用,没有发挥泛型的优势。
// 编译器发生警告:
List list = new ArrayList();
list.add("Hello");
list.add("World");
String first = (String) list.get(0);
String second = (String) list.get(1);
当我们定义泛型类型<String>后,List<T>的泛型接口变为强类型List<String>。
// 无编译器警告:
List<String> list = new ArrayList<String>();
list.add("Hello");
list.add("World");
// 无强制转型:
String first = list.get(0);
String second = list.get(1);
当我们定义泛型类型<Number>后,List<T>的泛型接口变为强类型List<Number>。
List<Number> list = new ArrayList<Number>();
list.add(new Integer(123));
list.add(new Double(12.34));
Number first = list.get(0);
Number second = list.get(1);
2. 编译器如果能自动推断出泛型类型,就可以省略后面的泛型类型,甚至省略后面的泛型类型后尖括号也可以省略。例如,对于下面的代码:
List<Number> list = new ArrayList<Number>();
可以省略后面的泛型类型转化为
// 这时需要编译器自己推断泛型类型
List<Number> list = new ArrayList<>();
// 甚至省略后面的泛型类型后尖括号也可以省略
List<Number> list = new ArrayList();
3. 在接口中使用泛型
4. 编写泛型类
// 定义类 Apple 时使用了泛型声明
class Apple<T> {
// 使用T类型形参定义实例属性
private T info;
public Apple() {
}
// 下面方法中使用T类型形参来定义构造函数
public Apple(T info) {
this.info = info;
}
public void setInfo(T info) {
this.info = info;
}
public T getInfo() {
return this.info;
}
}
public class GenericLearn {
public static void main(String[] args) {
// 由于传给T形参的是String,所以构造器参数只能是String
Apple<String> a1 = new Apple<>("苹果");
System.out.println(a1.getInfo());
// 由于传给T形参的是Double,所以构造器参数只能是Double或double
Apple<Double> a2 = new Apple<>(100.06);
System.out.println(a2.getInfo());
}
}
---------------------------------------------------------
Apple
100.06
尖括号放置说明:
20210428205844.jpg
5. 泛型类中的静态方法使用泛型
编写泛型类时,要特别注意,泛型类型<T>不能用于静态方法。无法在静态方法的方法参数和返回类型上使用泛型类型T。那我们该怎么办呢?我们可以这样对于静态方法,我们可以单独改写为“泛型”方法,只需要使用另一个类型即可。例如下面这样:
// 编译时这将出现无法在静态上下文上引用非静态类型变量T的错误
public static Apple<T>readObject(T a) {
return new Apple<T>(a);
}
单独改写为“泛型”方法后的
public static <K> Apple<K>readObject(K a) {
return new Apple<K>(a);
}
具体实现:
// 定义类 Apple 时使用了泛型声明
class Apple<T> {
// 使用T类型形参定义实例属性
private T info;
public Apple() {
}
// 下面方法中使用T类型形参来定义构造函数
public Apple(T info) {
this.info = info;
}
public void setInfo(T info) {
this.info = info;
}
public T getInfo() {
return this.info;
}
public static <K> Apple<K>readObject(K a) {
return new Apple<K>(a);
}
}
public class GenericLearn {
public static void main(String[] args) {
// 由于传给T形参的是String,所以构造器参数只能是String
Apple<String> a1 = new Apple<>("Apple");
System.out.println(a1.getInfo());
// 由于传给T形参的是Double,所以构造器参数只能是Double或double
Apple<Double> a2 = new Apple<>(100.06);
System.out.println(a2.getInfo());
Apple<Number> a3 = Apple.readObject(2021);
System.out.println(a3.getInfo());
}
}
----------------------------------------------
Apple
100.06
2021
6. 支持多个泛型类型
泛型还可以定义多种类型。例如,我们希望Apple不总是存储两个类型一样的对象,就可以使用类型<T, K>同时指定泛型参数类型,例如像下面这样:
class Apple<T, K> {
private T phone;
private K series;
public Apple(T phone, K series) {
this.phone = phone;
this.series = series;
}
public T readPhone() {
return phone;
}
public K readNumber() {
return series;
}
}
public class GenericLearn {
public static void main(String[] args) {
// 使用的时候,需要指出两种类型 [String, Integer]
Apple<String, Integer> a1 = new Apple<>("iphone", 12);
System.out.println(a1.readPhone());
System.out.println(a1.readNumber());
}
}
-----------------------------------------------------------------
iphone
12
网友评论