如果你追求新的JDK,那么当你习惯了JDK1.4 的编码后,使用JDK1.5一定出现很多问题。其中,使用泛型的警告一定是布满整个程序吧( 可用@SuppressWarnings("unchecked")消除警告),于是便打算看看泛型的知识。结果不看不知道,一看吓一跳。
先说说泛型的语法吧。
首先,一个类,接口都可泛型。语法是: public class classname<T> , public interface interfacename<T>
其次,泛型可用于函数(方法)。语法为: public <T> void f(T x) 或静态方法 public <A,B> Map<A,B> f()
最后,泛型可以定界。例如 public class ClassRoom<T extends Room<E>> 。 ClassRoom现在接受一个Room类型的对象。
在说说泛型的问题:
首先,由于擦除,泛型的类型信息都在编译后失去了。
例如:
public
class
EraserCheck
{
public static void main(String args[]){
Class c1 = new ArrayList<String>().getclass();
Class c2 = new ArrayList<Integer>().getclass();
System.out.println(c1 == c2);
}
}
你认为输出的是true还是false?
事实上,ArrayList<String>与ArrayList<Integer>是截然不同的ArrayList<Integer>中是不能放入String的。
这是为什么呢?
我们再来看看下面一个例子。
public
class
LostInformation
{
public static void main(String[] args){
List<String> list = new ArrayList<String>();
Map<Integer, String> map = new HashMap<Integer, String>Map();
System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
}
}
根据JKD文档描述,Class.getTypeParameters 将返回一个TypeVariable对象的数组,表示有泛型声明所声明的
参数类型。事实上呢,输出了什么?
你自己试试,并对比一下ArrayList的源代码和HashMap的源代码。实际上输出的只是占位符号。
来看看以下的C++代码,摘自《Think in java 4》,稍有改动。
#include
<
iostream
>
using
namespace
std:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
template
<
Class T
>
class
Manipulator
{
T obj;
public:
Manipulator(T x) {obj = x; }
Manipulator() {obj = new T();}
void manipulator() { obj.f(); }
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
class
HasF
{
public:
void f() { count << "HashF::f() " << endl;
};
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
int main(){
HasF hf;
Manipulator<HasF> manipulator(hf);
manipulator.mainpulate();
}
如果你没有学过C++模板,你一定会惊奇,为什么Manipulator的obj可以调用f(),因为他根本不知道obj会有这个函数阿。原因是编译期间编译器能知道Manipulator的模板(泛型)为HasF类,于是在HasF中自动寻找f()这个函数,如果没有,则出错。
而java却不能这样。问题就出在当编译器去寻找Manipulator的具体类的时候,具体类的信息丢失了,于是不知道从何寻找f()这个函数。
幸好我们还可以使用协助泛型类,看下面的java实现代码:
public
class
Manipulator
<
T
extends
HasF
>
{
T obj;
public Manipulator(T obj){this.obj = obj; }
public void manipulate(){obj.f();}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
public static void main(String[] args){
HasF hf = new HasF();
Manipulator<HasF> manipulator = new Manipulator<HasF>(hf);
manipulator.manipulate();
}
}
HasF省略。
由此已可看到擦除的问题了,但是为什么要用擦除呢?
这个就得从兼容性说起了。为了兼容jdk1.4的版本,当我们使用那些用1.4编写的类库或者其他资源时,我们可以
不必再使用jdk5.0来重写,或许这就是牺牲泛型,使用擦除的原因吧。当然,也提供了擦除的补偿。大家可以实现自己的补偿,使用各种方法。这里提一个函数isInstance(obj), 由于擦除,instancof已经不起作用了,所以
我们用isInstance来补偿。看下面的java代码(来自《Think in java4》:
public
class
Building
{}
public
class
House
{}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
public
class
ClassTypeCapture
<
T
>
{
Class<T> kind;
public ClassTypeCapture(Class<T> kind){
this.kind = kind;
}
public boolean f(Object obj){
return kind.isInstance(obj);
}
public static void main(String[] args){
ClassTypeCapture<Building> cttb = new ClassTypeCapture<Building>();
System.out.println(cttb.f(new Building()));
System.out.println(cttb.f(new House()));
ClassTypeCapture<House> cttbh = new ClassTypeCapture<House>();
System.out.println(ctth.f(new Building()));
System.out.println(ctth.f(new Housse()));
}
}
让我们来看看结果:
true
true
false
true
isInstance是一个动态信息,在执行的时候才检测。
还有一个问题,不知道大家发现没有,就是在定义一个泛型时如下:
public <K,V> Map<K,V> f(){
...
}
使用的时候,我们不得不在赋值语句两边都写类型参数,这样似乎很不合理。
Map<Integer,String> map = new HashMap<Integer, String>();
这样,Integer,String写了两次,显得编译器很笨。这也是擦除的原因。
大家自己看看有什么办法可以不用这样写呢?
java泛型的问题还很多,现在就到这里,我们下次在说。
先说说泛型的语法吧。
首先,一个类,接口都可泛型。语法是: public class classname<T> , public interface interfacename<T>
其次,泛型可用于函数(方法)。语法为: public <T> void f(T x) 或静态方法 public <A,B> Map<A,B> f()
最后,泛型可以定界。例如 public class ClassRoom<T extends Room<E>> 。 ClassRoom现在接受一个Room类型的对象。
在说说泛型的问题:
首先,由于擦除,泛型的类型信息都在编译后失去了。
例如:
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
事实上,ArrayList<String>与ArrayList<Integer>是截然不同的ArrayList<Integer>中是不能放入String的。
这是为什么呢?
我们再来看看下面一个例子。
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
参数类型。事实上呢,输出了什么?
你自己试试,并对比一下ArrayList的源代码和HashMap的源代码。实际上输出的只是占位符号。
来看看以下的C++代码,摘自《Think in java 4》,稍有改动。
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
如果你没有学过C++模板,你一定会惊奇,为什么Manipulator的obj可以调用f(),因为他根本不知道obj会有这个函数阿。原因是编译期间编译器能知道Manipulator的模板(泛型)为HasF类,于是在HasF中自动寻找f()这个函数,如果没有,则出错。
而java却不能这样。问题就出在当编译器去寻找Manipulator的具体类的时候,具体类的信息丢失了,于是不知道从何寻找f()这个函数。
幸好我们还可以使用协助泛型类,看下面的java实现代码:
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
由此已可看到擦除的问题了,但是为什么要用擦除呢?
这个就得从兼容性说起了。为了兼容jdk1.4的版本,当我们使用那些用1.4编写的类库或者其他资源时,我们可以
不必再使用jdk5.0来重写,或许这就是牺牲泛型,使用擦除的原因吧。当然,也提供了擦除的补偿。大家可以实现自己的补偿,使用各种方法。这里提一个函数isInstance(obj), 由于擦除,instancof已经不起作用了,所以
我们用isInstance来补偿。看下面的java代码(来自《Think in java4》:
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
true
true
false
true
isInstance是一个动态信息,在执行的时候才检测。
还有一个问题,不知道大家发现没有,就是在定义一个泛型时如下:
public <K,V> Map<K,V> f(){
...
}
使用的时候,我们不得不在赋值语句两边都写类型参数,这样似乎很不合理。
Map<Integer,String> map = new HashMap<Integer, String>();
这样,Integer,String写了两次,显得编译器很笨。这也是擦除的原因。
大家自己看看有什么办法可以不用这样写呢?
java泛型的问题还很多,现在就到这里,我们下次在说。