来自《Effective Java》介绍泛型的一段话。在没有泛型之前,从集合中读取到的每一个对象都必须进行转换。如果有人不小心插入了类型错误的对象,在运行时的转换处理就会出错。有了泛型之后,可以告诉编译器每个集合中接受哪些对象类型。编译器自动地为你的插入进行转化,并在编译时告知是否插入了类型错误的对象。
最能表示是Java 1.5版本之前的集合类。集合中每个方法传参都是Object类,毕竟类型不确定。反正Object是上帝类,也符合集合能装载任何Object类的子类。比如下面的示例,编译时不会报错,因为能接收Object类型。不过你遍历集合操作的时候,你就需要一个个强转相应的类型。非常麻烦。
//1.5版本之前
List list = new ArrayList();
list.add(new String());
list.add(new A());
list.add(new B());
String str = (String) list.get(0);
A a = (A) list.get(1);
B b = (B) list.get(2);
谁让你把不同的类型放在一个集合里面,实际开发难免会遇到这种问题,传过来List类,但是不知道要里面到底装的是什么类型。后来1.5版本引进了泛型,改进了集合,只允许同类型的进来。如果类型不对,编译时就会提示错误。提高安全性。编译无误,会将泛型擦除。
1.5版本之后
List<String> list = new ArrayList<String>();//只允许传String类型
Map<String,Demo> map = new HashMap<String,Demo>();//只允许传key为String类型,value为Demo类型
泛型,其实泛型就是标识,把具体类型抽象化。在类内部的抽象化类型称为类型形参,类外部称为类型实参。跟方法内的形参和方法外的实参的意思一样。使用泛型,首先要声明,泛型类的声明在类名后面。比如下面的示例,有T的地方就可以看做成指定那个类型。
public class Test {//声明T类型形参
T t;
}
//创建Test对象时,就可以加上泛型
Test test = new Test();//指明类型实参是String
//实际上相当于T都换成了String
public class Test {//声明此类标识泛型
String t;//指定的类型
}
为什么是T呢?多个类型形参要怎么做。泛型就是标识,用于取代具体类型。标识就跟变量名一样。要是多个类型形参只要用逗号隔开就好了。
public class Test<_123,ABC> {//声明多个类型形参
_123 _1234;
//指定ABC类型也不一定要使用
}
接口相当于类,同样可以使用。用法跟泛型类一样。只要指定类型实参,接口暴露出来也就那类型实参。非常干净。
public interface ITest {
void getType(T t);
}
new ITest(){//指定类型实参为String
public void getType(String t) {//返回的类型也为String
}
};
泛型不止与用在类,也可以用于方法。别忘了都要声明。还可以标识返回指定那类型,通常用于强转,省下不用在外部加上括号的麻烦。
public void printString(T t){
t.toString();//打印指定类型
}
public T getT(String s){
return (T) s;//将String s强转T类型并且返回
}
声明类型形参是解决了表明安全性,但是会发现类型形参跟Object类没任何区别。因此我们可以做限制。可以声明类型形参是某类的子类,或者是某类的父类。extends表上界,super表下界,?表通配符。? extends T叫上界通配符,? super T叫下界通配符。
> ///指定任意类型
<T extends Light> ///指定类型是Light的子类
<T super Light> ///指定类型是 Light的父类
extends Light> ///指定类型是 Light的子类
super Light> ///指定类型是 Light的父类
extends T> ///指定类型是 T的子类
super T> //指定类型是 T的父类
额。这么多可以看着头晕。来分个类吧。在类内部?和super是无法来用声明指定类型。因为编译器根本不知道你指定什么鬼类型。只能确定指定类型是否为某类的子类。只有extends还可以同于声明。
public class Test<T extends Light>
//以下编译错误
public class Test<T super Light>
public class Test<T super ?>
还可以使用方法中的声明,并且能调用父类的方法。
public void test(T t){//表明类型形参T是Light类的子类
t.getLightValus();//当类型形参的是继承Light类,类型形参就可以调用Light类的方法
}
那?和super的作用在哪呢。其实是声明的外面,类的外面。extends在外面也可以使用。
//已经Test声明T必须为Light的子类时
Test> test = new Test>();
Test<Light> test = new Test<Light>();
Test extends Light> test = new Test<Light>();
//以下编译会报错
Test<A> test = new Test<A>();
Test> test = new Test>();
只有声明类型形参的时候还可以使用extends,类型实参三个都可以。掌握之后,可以写出非常优雅的代码。
总的来说,泛型目的就是加强安全性,提升灵活性,简化了代码。写到这里,不足之处望指教。