Java中的泛型可以说让新手一头雾水,而且多个泛型同时使用会让人一脸懵逼。现在就来看看各个情况吧。
1.最简单的泛型举例
public class TTest<T> {
public void test(){
List<T> list = new ArrayList<>();
}
public static void main(String args[]){
TTest<String> t = new TTest<String>();
t.test();
}
}
这个就是最简单的泛型,可以看到在TTest类定义的地方写了一个<T>,这就是泛型的定义,类里面方法(静态方法除外),都可以使用这个类,具体是什么意思呢?
可以在main()方法里面看到,TTest<String> t ,这里就是把<String>传入了进去,test()方法里面的List<T> t = new ArrayList<>();其实就变成了List<String> t = new ArrayList<>();泛型的主要意义,也就是为了限定或者给定某个类型。当然泛型的这个<T>名字可以随便取比如<U>,<X>等等。
2.多个泛型的使用
public class TTest<T, U> {
public void test(){
List<T> tList = new ArrayList<>();
List<U> uList = new ArrayList<>();
}
public static void main(String args[]){
TTest<String, Integer> t = new TTest<>();
t.test();
}
}
这里使用了两个泛型<T, U>,在传入的时候,写入TTest<String,Integer> t 然后在类里面就可以去轻松愉快的使用了,两个泛型也是比较常见的,比如HashMap<K, V>。
3.泛型的继承
public class TTest<T extends TTest.Base> {
public static class Base{
}
public static class Child extends Base{
}
public void test(){
List<T> tList = new ArrayList<>();
}
public static void main(String args[]){
TTest<String> st = new TTest<>();//编译错误
TTest<Base> bt = new TTest<>();//编译正确
TTest<Child> ct = new TTest<>();//编译正确
}
}
这里可以看到<T extends TTest.Base>也就是说,我传入的泛型是要继承自Base类的,所以在new TTest的时候可以看到,如果写了TTest<String>就会编译错误,因为String不是继承自Base的。写成TTest<Base>或者TTest<Child>都是正确的,因为Child是继承自Base的。
4.在静态方法中泛型的使用
public class TTest<T> {
public static <M> List<M> get(){
Class<M> m;//编译正确
Class<T> t;//编译错误
return null;
}
public void test(){
TTest<Integer>.<String>get();//编译错误,因为调用静态方法不需要写类的泛型
TTest.<String>get();//编译正确
}
}
因为静态方法其实是属于类的,不属于对象,所以普通定义的泛型,在静态方法里面是无法使用的,会编译错误,比如在上面代码中get()方法里面的Class<T>就会编译错误。
在如果要在静态方法中使用的泛型的话,需要重新定义,在static关键字和返回类型中间添加泛型<M>,然后在返回类型或者方法体中,就可以去使用这个泛型了。在使用的时候,就用TTest.<String>get();去调用,public static <M> List<M>get()方法就会变成public static List<String>get(),方法体里面的Class<M>也就变成了Class<String>,其实就是把<M>替换成了<String>。
5.普通方法中使用泛型
@Nullable
public final <T extends View> T findViewById(@IdRes int id) {
if (id == NO_ID) {
return null;
}
return findViewTraversal(id);
}
protected <T extends View> T findViewTraversal(@IdRes int id) {
if (id == mID) {
return (T) this;
}
return null;
}
这里是Android源码中的findViewById()方法,方法中在返回类型T前定义了<T>,这样就可以在最后return时,把类型强制转化为T。这样写的好处是,不需要每次在调用findViewById()后去强制转化为TextView或ImageView了。
小节:
前面讲到了好几种泛型的使用场景,使用泛型的好处就是,你知道了传入的类型,就可以在方法定义时直接转为想要的类型,而不需要方法的调用者再去强转。