-
第一部分:泛型说明
泛型是Java SE1.5的新特性。
泛型的本质是类型参数化。也就是操作的数据类型是个参数,不是确定的类型,在使用时指定。
这种类型的参数可以用在类、接口、方法中,分别称为泛型类、泛型接口、泛型方法。
Java引入泛型的好处是安全简单。在Java SE1.5之前,没有泛型的情况下,通过类型Object的使用,实现参数类型的任意化。“任意化”带来的缺点就是要做显示的强制类型转换。如果强制类型转换错误,编译器不会提示。在运行的时候才会出现异常,这是一个安全隐患。
泛型的好处是编译的时候检查类型安全,并且所有的强制转换都是自动和隐藏的,提高代码的重用率。
-
泛型的代码示例:
-
原始代码
public class DoubleTest {
public DoubleTest(Double var) {
this.var = var;
}
public Double getVar() {
return var;
}
public void setVar(Double var) {
this.var = var;
}
private Double var;
}
public class StringTest {
public StringTest(String var) {
this.var = var;
}
public String getVar() {
return var;
}
public void setVar(String var) {
this.var = var;
}
private String var;
}
上面的类中,成员和方法的逻辑都一样,就是类型不一样,因此考虑重构。Object是所有类的父类,因此可以考虑用Object做为成员类型,这样就可以实现通用了,实际上就是“Object泛型”,暂时这么称呼。
-
没有使用泛型
public class WithoutGenerics {
public WithoutGenerics(Object var) {
this.var = var;
}
public Object getVar() {
return var;
}
public void setVar(Object var) {
this.var = var;
}
public void showTypeName() {
System.out.println(String.format("参数实际类型是:%s", var.getClass().getSimpleName()));
}
private Object var;
public static void main(String[] args) {
WithoutGenerics w1 = new WithoutGenerics(1);
w1.showTypeName();
// 使用变量时,要显示进行强制类型转换。
Integer var1 = (Integer)w1.getVar();
System.out.println(String.format("参数值是:%s", var1));
WithoutGenerics w2 = new WithoutGenerics("123");
w2.showTypeName();
// 使用变量时,要显示进行强制类型转换。
String var2 = (String)w2.getVar();
System.out.println(String.format("参数值是:%s", var2));
WithoutGenerics w3 = new WithoutGenerics(w1);
w3.showTypeName();
}
}
在Java 5之前,为了让类有通用性,往往将参数类型、返回类型设置为Object类型,当获取这些返回类型来使用时候,必须将其“强制”转换为原有的类型,然后才可以调用对象上的方法。
-
使用泛型
强制类型转换很麻烦,需要事先知道各个Object具体类型是什么,才能做出正确转换。否则,要是转换的类型不对,比如将“Hello Generics!”字符串强制转换为Double,那么编译的时候不会报错,可是运行的时候就挂了。那有没有不强制转换的办法----有,改用 Java5泛型来实现。
public class WithGenerics <T> {
public WithGenerics(T var) {
this.var = var;
}
public T getVar() {
return var;
}
public void setVar(T var) {
this.var = var;
}
public void showTypeName() {
System.out.println(String.format("参数的实际类型:%s", var.getClass().getSimpleName()));
}
private T var;
public static void main(String[] args) {
WithGenerics<Integer> w1 = new WithGenerics<>(Integer.valueOf(1));
w1.showTypeName();
// 1. 使用变量时,不需要显示进行强制类型转换。
Integer var1 = w1.getVar();
System.out.println(String.format("参数值是:%s", var1));
WithGenerics<String> w2 = new WithGenerics<>("11234");
w2.showTypeName();
// 2. 使用变量时,不需要显示进行强制类型转换。
String var2 = w2.getVar();
System.out.println(String.format("参数值是:%s", var2));
// 3. 如果没有声明实际的参数类型
// 使用变量时,需要显示进行强制类型转换。
WithGenerics w4 = new WithGenerics<>(Long.valueOf(123));
w4.showTypeName();
Object w4Var = w4.getVar();
}
}
使用<T>来声明一个类型持有者名称,然后就可以把T当作一个类型代表来声明成员、参数和返回值类型。T仅仅是个名字,这个名字可以自行定义。
class WithGenerics<T> 声明了一个泛型类,这个T没有任何限制,实际上相当于Object类型,实际上相当于 class WithGenerics<T extends Object>。
与Object泛型类相比,使用泛型所定义的类在声明和构造实例的时候,可以使用“<实际类型>”来一并指定泛型类型持有者的真实类型。类如WithGenerics<Long> w4 = new WithGenerics<>(Long.valueOf(123));
当然,也可以在构造对象的时候不使用尖括号指定泛型类型的真实类型,但是你在使用该对象的时候,就需要强制转换了。比如:WithGenerics w4 = new WithGenerics<>(Long.valueOf(123));
实际上,当构造对象时不指定类型信息的时候,默认会使用Object类型,这也是要强制转换的原因。
-
泛型的语法限制
- 泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
- 泛型的类型参数可以有多个。
- 泛型的参数类型可以使用extends语句。例如<T extends superClass>。习惯称为“有界类型”。
- 泛型的类型参数可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");
通过上面的说可以说明第二篇文章(http://www.jianshu.com/writer#/notebooks/20714250/notes/22009385)中的改写为什么没有效果。自定义T类型,相当于 T extends Object。所有的类型都继承自Object。所以编译器检查类型时没有问题。
网友评论