Java泛型下――万恶的擦除(二)

2014-11-23 22:59:11 · 作者: · 浏览: 1
)。在main中将Dog作为类型参数传递给Animal。而在代码的第8行和第11行,我们尝试调用传入类的函数move()和bark(),发现会有错误;在代码16行,我们试图返回一个T类型的对象即new一个,也会得到错误;而在代码20行,当我们试图利用instanceof判断T是否为Dog类型时,同样是错误!
所以还是上面我们说过的话:在泛型代码内部,无法获得任何有关泛型参数类型的信息 (摘自《Java编程思想第4版》),我们在编写泛化类的时候,我们要时刻提醒自己,我们传入的参数T仅仅是一个Object类型,任何具体类型信息我们都是未知的。
二、为什么Java用擦除
上面我们简单阐述了Java中泛型的一个擦除问题,也体会到它的万恶,给我们编程带来的不便。那Java开发者为什么要这么干呢?
这是一个历史问题,Java在版本1.0中是不支持泛型的,这就导致了很大一批原有类库是在不支持泛型的Java版本上创建的。而到后来Java逐渐加入了泛型,为了使得原有的非泛化类库能够在泛化的客户端使用,Java开发者使用了擦除进行了折中。
所以Java使用这么具有局限性的泛型实现方法就是从非泛化代码到泛化代码的一个过渡,以及不破坏原有类库的情况下,将泛型融入Java语言。
三、怎么解决擦除带来的烦恼
解决方案1:
不要使用Java语言。这是废话,但是确实,当你使用python和C++等语言,你会发现在这两种语言中使用泛型是一件非常轻松加随意的事情,而在Java中是事情要变得复杂得多。如下示例:
python:
复制代码
1 class Dog:
2 def speak(self):
3 print "Arf!"
4 def sit(self):
5 print "Sitting"
6 def reproduce(self):
7 pass
8
9 class Robot:
10 def speak(self):
11 print "Click!"
12 def sit(self):
13 print "Clank!"
14 def oilChange(self) :
15 pass
16
17 def perform(anything):
18 anything.speak()
19 anything.sit()
20
21 a = Dog()
22 b = Robot()
23 perform(a)
24 perform(b)
复制代码
python的泛型使用简直称得上写意,定义两个类:Dog和Robot,然后直接用anything来声明一个perform泛型方法,在这个泛型方法中我们分别调用了anything的speak()和sit()方法。
C++
复制代码
1 class Dog {
2 public:
3 void speak() {}
4 void sit() {}
5 void reproduce() {}
6 };
7
8 class Robot {
9 public:
10 void speak() {}
11 void sit() {}
12 void oilChange() {
13 };
14
15 template void perform(T anything) {
16 anything.speak();
17 anything.sit();
18 }
19
20 int main() {
21 Dog d;
22 Robot r;
23 perform(d);
24 perform(r);
25 } ///:~
复制代码
C++中的声明相对来说条条框框多一点,但是同样能够实现我们要达到的目的
Java:
复制代码
1 public interface Performs {
2 void speak();
3 void sit();
4 } ///:~
5 class PerformingDog extends Dog implements Performs {
6 public void speak() { print("Woof!"); }
7 public void sit() { print("Sitting"); }
8 public void reproduce() {}
9 }
10 class Robot implements Performs {
11 public void speak() { print("Click!"); }
12 public void sit() { print("Clank!"); }
13 public void oilChange() {}
14 }
15 class Communicate {
16 public static void perform(T performer) {
17 performer.speak();
18 performer.sit();
19 }
20 }
21 public class DogsAndRobots {
22 public static void main(String[] args) {
23 PerformingDog d = new PerformingDog();
24 Robot r = new Robot();
25 Communicate.perform(d);
26 Communicate.perform(r);
27 }
28 }
复制代码
Java代码很奇怪的用到了一个接口Perform,然后在代码16行定义泛型方法的时候指明了(泛型方法的声明方式请见:【Java心得总结三】Java泛型上——初识泛型),声明泛型的时候我们不是简单的直接而是确定了一个边界,相当于告诉编译器:传入的这个类型一定是继承自Perform接口的,那么T就一定有speak()和sit()这两个方法,你就放心的调用吧。
可以看出Java的泛型使用方式很繁琐,程序员需要考虑很多事情,不能够按照正常的思维方式去处理。因为正常我们是这么想的:我定义一个接收任何类型的方法,然后在这个方法中调用传入类型的一些方法,而你有没有这个方法,那是编译器要做的事情。
其实在python和C++中也是有这个接口的,只不过它是隐式的,程序员不需要自己去实现,编译器会自动处理这个情况。
解决方案2:
当然啦,很多情况下我们还是要使用Java中的泛型的,怎么解决这个头疼的问题呢?显示的传递类型的Class对象: