泛型定义
泛型,即”不确定的参数类型”,使代码可应用于多种不同的类型,而不再是仅限于一种确定的类型。泛型使我们可以通过参数确定类要使用的类型。
泛型的使用方法
泛型类
泛型类的定义需要将类型参数列表放在类名的后面
class className<Type1,Type2...>{}
例:
public class Test<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public static void main(String[] args){
Test<A> test = new Test<A>();
test.setT(new A());
test.getT().showMessage();
}
}
class A{
public void showMessage() {
System.out.println("This is class A.");
}
}
//运行结果:
//This is class A.
在创建Test实例的时候,我传进去一个类型参数A ,这样Test就知道我需要用这个类是什么类型,并且我不需要对取出的t进行强转,它依旧可以执行这个类自己的方法,因为它返回的就是一个A的实例,而不是Object。
泛型方法
泛型方法需要将类型参数列表放在返回值的前面,使用时可以用在返回值上,也可以用在方法参数中
<Type1,type2> returnType methodName(){}
例:
public class Test {
public static <T> void viewClassMsg(T t){
System.out.println(t);
}
public static void main(String[] args){
viewClassMsg(new A());
}
}
class A{
@Override
public String toString() {
return "This is class A.";
}
}
//运行结果
//This is class B.
这样,我们就可以通过泛型来指定应用需要使用的类型,而不需要使用具体的类型。
泛型通配符
在声明变量及定义方法时可以使用泛型通配符“?”,表示任意泛型类型,但是取出时要小心,正因为能表示任意泛型类型,它不能确定它取出来的是否和你所需要的那种,所以它只能以Object的方式取出来。
如图所示,程序无法找到view这个方法,因为它不确定取出来的这个值是什么类型,所以不能判断是否拥有这个方法。
上界与下界
使用泛型通配符范围过大,或许我们并不需要这么大的范围,仅仅需要限定泛型的一小范围,那么我们可以使用extends/super来进行限制
- 上界:extends
使用extends关键字可限制泛型的上界,即只允许参数类型为继承了某个类或实现了某个接口的类。
public class Test{
public static void viewMsg(List<? extends A> list){
for(A element : list){
System.out.println(element.msg);
}
}
public static void main(String[] args){
List<A> list0 = new ArrayList<>();
list0.add(new A());
viewMsg(list0);
List<B> list1 = new ArrayList<>();
list1.add(new B());
viewMsg(list1);
}
}
class A{
protected String msg = "蠢猪镜";
}
class B extends A{
}
//运行结果:
//蠢猪镜
//蠢猪镜
可以看到,不仅是list0可以传递进去,list1也可以,因为限制了泛型的上界,只要是持有类型A及其子类的List都可以作为参数传递进去,这就是所谓的上界。
- 下界:super
使用super关键字可以限制泛型的下界,即限制泛型的类型参数为指定类的本身或其父类。
public class Test{
public static void viewMsg(List<? super B> list){
for(Object element : list){
System.out.println(element);
}
}
public static void main(String[] args){
List<A> list0 = new ArrayList<>();
list0.add(new A());
viewMsg(list0);//将List<A>的实例传递进去,可行
List<B> list1 = new ArrayList<>();
list1.add(new B());
viewMsg(list1);//将List<B>的实例传递进去,可行
List<C> list2 = new ArrayList<>();
list1.add(new C());
//viewMsg(list2);---这里报错,因为C是B的子类,而泛型要求为B及其父类
}
}
class A{
@Override
public String toString() {
return "蠢猪镜";
}
}
class B extends A{
@Override
public String toString() {
return "蠢海星";
}
}
class C extends B{
}
在遍历List中元素时,我尝试使用B或者A去作为增强for的需要遍历的类型,但是却报错了。后来我想清楚了,因为使用的这些类在其上还有父类,而子类中可能拥有一些父类没有的值或方法,这样就不能直接用这种还有父类的类去作为遍历的类型,需要使用Object类。
总结
泛型的出现给开发带来了许多的便利,让我们可以在一段代码中使用不同的类型,而不用去针对一个类型去写大量相同的代码。并且使用泛型也使代码更加简洁易读。但是在泛型中,还有其他的东西需要去学习,如泛型的类型擦除,类型擦除是泛型实现过程中一个重要的机制,下一个博客将会对类型擦除作出详细的讲解。