有一段代码如下
1 | public <T> T get(String key, T def) { |
代码很简单,mContainer是Map类型,key为String,value为Object类型,当调用get方法时,传入一个key,参一个默认值,不存在时则直接返回默认值。
我一直认为这种写法是比较危险的做法,对于上面的方法可能还好,但对于下面的写法,泛型可以认为毫无用处:
1 | public <T> T get(String key) { |
由于我知道mContainer里对应key的类型是什么,所以在get的时候就可以使之赋值到对应的类型中,如
1 | long ret = get("test"); |
两者是完全可以编译过的,假如map里的value是int型,那两个执行起来都没问题,但如果value是long型,第二句就会发生错误了,错误为类型转换失败(long -> int)。
发生这种问题主要还是因为泛型和自动类型转换造成的。
1 | <T> T get(String key, T def) |
可以分成几个部分
- <T>: 声明这个方法需要使用到泛型的类型,这里只有一个,也可以声明多个,如<T,K>;
- T : 返回的类型,为泛型T(不确定类型),最后编译后会成为Object类型;
- T def: 传入一个参数类型为泛型的对象,编译后其实不管这个def是什么类型,最后都会变成Object的,所以在调用时可以传入任意的对象类型
以前的博文也有说过,泛型发生作用都是在编译时,还是拿T get(String,T)方法来举例:
1 | long ret = get("test",1L); |
这个编译是可以通过的,但
1 | int ret = get("test",1); |
是不可能通过编译的。原因在于对于get方法来说,第二个参数与返回的类型应该都是一致的(或者可以自动向上转换的),编译才能通过,如
1 | long ret = get("test",1); |
就可以通过,因为int型可以向上转为long型,但反过来就不行了。
对于只有一个确定参数类型的get方法 T get(String) 则不能发挥这种编译时检查的功能,因为这个类型T没有找到与之相匹配的,可检查对比的参数,这种写法与
1 | Object get(String key) |
是等价的,无论是从编译时还是运行时。