泛型的基本概念看上面文章就行,说几点看开源库中看到的泛型用法:
boolean hasLoadPath(Class<?> dataClass) {
return getLoadPath(dataClass) != null;
}
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
hasLoadPath是一个普通方法,需要一个Class类型的对象,泛型是?,注意啦,?是一个类型实参,和Number、Integer等类是一样的,表示能接受任何Class对象,比如Number.class、ByteBuffer.class。
getLoadPath是一个泛型方法,自己定义的泛型是<Data>,Transcode是泛型类中定义的,看到没?可以一块用,不影响。Class<Data> dataClass表示在调用的时候传一个Class对象,Data就确定了。比如上面的代码,Data是?
我们看看hasLoadPath的调用:hasLoadPath(ByteBuffer.class),进入hasLoaPath方法中,dataClass还是Class<?>,除非(Class<ByteBuffer>)dataClass强转一下,就好像父类转子类一样。
我们在定义方法的时候,可以定义参数,比如methd(int a, int b),a和b叫做形参,调用是传入的叫实参,a和b是变量,变量天生是参数化的。为了便于理解泛型,我们也应该有个叫法,要不然不好理解和记忆,比如上面的decodeFromFetcher泛型方法,Data我们叫泛型形参,调用的时候,实际传入的叫泛形实参,Data本身是表达类型的,所以我们才说泛型就是把类型参数化。
我们再分析一下,下面的泛型方法:
public <Data, TResource> Registry append(
@NonNull String bucket,
@NonNull Class<Data> dataClass,
@NonNull Class<TResource> resourceClass,
@NonNull ResourceDecoder<Data, TResource> decoder) {
decoderRegistry.append(bucket, decoder, dataClass, resourceClass);
return this;
}
这个一看就是一个泛型方法,定义了Data和TResource两个泛型参数,在调用的时候才知道这两个泛型参数的具体类型,我们看看调用
.append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
通过调用,我们知道Data是ByteBuffer,TResource是Bitmap,这个没有问题,看看ResourceDecode<ByteBuffer, Bitmap>,decoder传入的是byteBufferBitmapDecoder
public class ByteBufferBitmapDecoder implements ResourceDecoder<ByteBuffer, Bitmap> {
}
说明:泛型类可以继承其它泛型类,而且是满足多态的。
不论是泛型类还是泛型方法,泛型是需要先定义的,比如:泛型类是在类名后面定义的,泛型方法是在返回值前面定义的
public <Model, Data> Registry append(
@NonNull Class<Model> modelClass, @NonNull Class<Data> dataClass,
@NonNull ModelLoaderFactory<Model, Data> factory) {
modelLoaderRegistry.append(modelClass, dataClass, factory);
return this;
}
class EngineJob<R> implements DecodeJob.Callback<R>,
Poolable {
...
private DecodeJob<R> decodeJob;
...
}
只有在定义的时候才可以指定上下界,或者用通配符?的时候也可以指定上下界。
Java的泛型实现是在编译器这个层次来实现的,在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉,这个过程叫类型擦除。正是因为类型擦除的原因,编译器会禁止泛型类型参数的隐含转换,比如List<String>要传给List<Object>就是泛型类型参数隐含转换,这样会导致运行期出错。
在看Glide源码的时候总是能看到如下操作:
SingleRequest<?> request = new SingleRequest<Object>;
(SingleRequest<R>)request;
我们用类型擦除考虑一下就明白了。
泛型还有更深的知识,尤其是Type,没看明白