jdk1.5之六 —— Generics —— 1 Basic

你可以说某某对象是属于某个类的,但某某类是属于?Java之前偷懒说所有的类其实都是个Object,咱Java不像C,C是没有唯一的基类的,所以必须要Template。可是用Object来包容所有类毕竟是继承的概念,与泛型(把类抽象化)的概念有很大差别,所以,Generic Types最终还是不可避免的来到了


——Fantasy Dog

Generic引进的最大原因是Type Safe(类型安全)。之前用Object表示所有类型的时候,必须用cast的方法将所需的类转换回来:
List wordList = new List();
wordList.add("Generic");
String word = (String)wordList.get(0);

这种强制转换(cast)的方式是不安全的,因为cast跳开了编译器的类型检查,编译时(Compile Time)无法发现错误,有也只能在运行的时候(RunTime)抛出一个ClassCastException。而更大的麻烦是错误源可能很难找——你有可能是在几百行代码之前错误的插入了一个其他类型的对象,慢慢找去吧。

靠Object转换来转换去的方法似乎可以做为Generic Types的替代方案,但这样一来一个致命的弱点就暴露了:Object等于完全跨越了类型安全这个C系语言引以为豪的核心功能,而java.util包下泛滥的Object参数更是对所谓完全面向对象的强类型语言Java的极大讽刺。
不明白C#这个东西为什么一开始也要学Java舍弃如此重要的Generic Types,就像中国在酝酿着一场和美国二三十年代如出一辙的经济大萧条一样,是出于无知还是无奈?
好在,该来的终究还是来了…………

1.简单的Generics
public interface Iterator<E>{
    E next();
    boolean hasNext();
    void remove();
}

这是一个泛型类,E表示任意的一个类型。与c的不同有以下几点:
a.形式上看,声明简化了,直接在类名后面加<E>(或多个类型参数如<E,F,G>),而不用写什么template <class Type>了。
b.Generics和c的template是有本质的区别的。在c里,你用template时,声明一个类如:Iterator<MyClass>,编译器实际上是要生成一个相应的新类的(Iterator_MyClass?),而Generics里面,就只有一个类Iterator。<E>的官方名字叫做Type parameter,就是说这个E是Iterator类的一个参数,使用者使用的时候具体指定。
c.Generics只接受类,即E必须是个类,而不能是primative的东西(如int)。因此如果你要个Iterator<int>时,只能用Iterator<Integar>。而此时,automatic boxing才真算是派上了用场——你大可以把primative的东西拿来用,细节问题编译器帮你处理。

Type Parameter的名字是有讲究的,倒不是必须,而是约定俗成的一个习惯——用完全大写的字母表示,越简单越好,最好是一个,如E。
这样的习惯当然有好处:成员是小写的,类名是大写打头后跟小写的,final值的变量是完全大写并且比较长的,Generics只能挑个比较有性格的表示方法来名字防止冲突,并借此保证代码的整洁。如Collections的Type Parameter通常都是E——element。

2.Generics与Subtyping
这是一个很恼人的问题,非常非常恼人(annoying)。
List<String> ls = new ArrayList<String>();
List<Object> lo = ls;
首先这说明了Generics怎么声明。然后,这样子好像很正确,因为String 就是个 Object,那我装String的东西当然也应该可以upcast成一个装Object的东西了?
lo.add(20);
这下完蛋了,因为lo虽然是个List<Object>,但它指向的是个List<String>,Integar的东西当然不能放在String的容器里。
这却不仅仅是Generics的问题:
String[] s = new String[2];
Object[] o = s;
o[0] = new Object();

这段代码编译通过,却在运行时给出一个ArrayStoreException。是啊,又是String的东西不能装Object。
深究一下:当我们把String[] upcast成一个Object[]时,这个Object[]仍然是一个String[],因此,它只能装String,而不是像其声明一样装Object。这么说来,面向对象的核心:多态原来是不安全的?

当然,发现棉花和铁块不同时落地就断定伽利略在比萨斜塔的实验是完全错误的是一种很幼稚的想法。看起来很惊世骇俗可以颠覆权威的发现往往总是因为其自身问题造成的(往往不代表全部,但是往往代表绝大多数)。
看看s和o。实际上,构成继承关系的是String和Object,而非String[] 和 Object[]。编译器之所以允许o=s,是因为它认为s的每一个元素都可以upcast成一个Object,结果却造成了类型安全的漏洞(当然,可以在RunTime查出来)。
再来看看什么叫多态。多态是指子类具有父类的全部功能,因此可以用一个对父类的引用来使用某个对象,而不必关心该对象是属于该父类的哪一个子类。
而我们的例子是另外一回事。当涉及到数组时,upcast后,可以做两件事:1,调用某个元素的Object功能如toString,此曰多态;2,在某个元素的位置插入一个Object或其子类的对象,这个……就是错误了。
同样的道理,在泛型里,String是Object的子类,但List<String>却不是List<Object>的子类,他们是相同的类,具有不同的Type parameter而已。编译器当然无法保证某个Type parameter在类内部是如何操作的,如果有如同上面数组那样的操作,就必然会产生非常隐蔽的错误,而此错误很难被跟踪。因此,编译器干脆就把List<Object> lo = ls;这条语句当作非法的。

窃以为,不同数组之间的相互转换(o=s)也应该被禁止。但估计在泛型出来之前,这个问题没被怎么重视。现在改也不行了,因为要保证向前兼容。咱们只能自己注意了。

然则之后Java干了件令人咂舌的事:
ArrayList<String>[] wordList = new ArrayList<String>[10];
这样的代码是被禁止的。原因是它无法保证你不会乱用这个数组:
Object[] lo = wordList;//不是继承的关系,但是这种upcast是被允许的(且为了向前兼容,不能被撤销)
ArrayList<Integar> li = new ArrayList<Integar>();
lo[0] = li;//插入一个ArrayList<Integar>,这种cast当然也被接受
li.add(123);
String ls = wordList[0].get(0);//错,运行时错,叫ClassCastException,不能把Integar cast成 String。

所以……java干脆就禁止你声明parameterized type的数组!(ArrayList<String>这样给了type参数的就叫做parameterized type)这不是典型的因噎废食嘛~~~没办法,问题的关键在于对数组的cast没有做限制,可是“向前兼容”这四个字牢牢地压在了java身上。唉~~~ 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值