android在api 26中的findViewById方法使用了泛型,使我们在开发中免了强转类型的苦恼,今天突然冒出了一个疑问(个人在之前对泛型还没有深入理解),故在此记录一下:
findViewById()怎么知道我要的是哪一种类型,也就是说它怎么能根据不同ID刚好强转成相对应的View。
1.一般来说方法泛型原型是这个样子的:
public <T extends View> T get(T t);
public <T extends View> void get(T t);
public <T,E> void get(T t,E e);
<>
里面是对参数t的约束和对类型T的声明,例如<T extends View>
声明了一种类型T,且T是View的子类,然后根据传入的参数做一些处理,这里T的意义是在于对参数T t
的约束和声明,任何View的子类都可以作为参数传进来进行处理。
2.看一下findViewById()函数原型:调用了函数findViewTraversal()
@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) {
//判断ID是否和自己相同,相同则强转返回
if (id == mID) {
return (T) this;
}
return null;
}
疑问产生了,findViewTraversal()方法参数是一个int值,T是一个声明的未知类型,怎么做到返回给我一个正确的类型的呢?(T) this
是强转了,但是强转的是个什么东西?只知道T是继承View的,如下代码,是怎么转强转ImageView的,它怎么知道我需要的是一个ImageView类型。
例如:
ImageView img = findViewById(R.id.img);
解谜:findViewById()其实并不能根据一个ID返还给你一个对的类型,什么意思呢?下面这两行代码都能编译通过并且运行:
ImageView img = findViewById(R.id.img);
Button button= findViewById(R.id.img);
这两行代码在编译后会是这个样子的:
ImageView img = (ImageView)findViewById(R.id.img);
Button button= (Button)findViewById(R.id.img);
也就是说该方法不知道你要什么,只是使用了泛型(T) this
进行了强制类型转换,告诉编译器在编译的时候该方法返回的是一个泛型类型,要进行类型转换,明显,强转为Button的时候程序在运行时会崩溃。
总结:其实在原理上findViewById()方法和之前版本是一样的,在编译之后,都需要进行了强转类型,只不过,api 26中使用了泛型,强转操作不用开发人员每次都手动进行,而是放在了编译过程中。