- 一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类。如果要编写可以适用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。
- 在面向对象编程语言中,多态是一种泛化机制。
- JavaSE5 的重大变化之一: 泛型的概念。泛型实现了参数化类型的概念,使代码可以应用于多种类型。泛型 这个术语的意思是 : 适用于许多许多的类型。
- 如果你了解其他语言(例如C++) 中的参数化类型机制,你就会发现,有些以前能够做到的事情,使用Java泛型机制缺无法做到。使用别人构建好的泛型类型相当容易。但是如果你要自己创建一个泛型实例,就会遇到很多令人吃惊的事情。
- 这并非说Java泛型毫无用处。在很多情况下,它可以使代码更直接更优雅。不过,瑞国你具备其他语言的经验,而那种语言实现了更纯粹的泛型,那么,Java可能令你失望了。
Java泛型与C++的比较
-
Java的设计者曾说过,设计这门语言的灵感主要来自C++。
-
了解C++模板的某些方面,有助于你理解泛型的基础。同时,非常重要的一点是,你可以了解Java泛型的局限是什么,以及为什么会有这些限制。最终目的是帮助你理解,Java泛型的边界在哪里。因为你只有知道某个技术不能做到什么,你这样才能更好地做到所能做的。
-
Java社区中,人们普遍对C++模板有了一种误解,而这种误解可能会误导你,令你在理解泛型的意图时产生偏差。
简单泛型
- 有很多原因促成了泛型的出现,最引人注目的一个原因是,就是为了创造容器类。容器,就是存放要使用的对象的地方。数组也是如此,不过与简单的数组相比,容器更加灵活,具备更多不同的功能。
//一个只能持有单个对象的类,这个类可以明确指定其持有的对象的类型 ,如下例子
public class Automobile {}
class Holder1{
private Automobile automobile;
public Holder1(Automobile automobile) {
this.automobile = automobile;
}
Automobile getAutomobile(){
return automobile;
}
}
- 这个类有以下问题 : ①可重用性不好 ②无法持有其他类型的任何对象
//JavaSE 5 之前,我们可以在类中直接持有Object类型的对象
public class Holder2 {
private Object object;
public Holder2(Object object) {
this.object = object;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
public static void main(String[] args) {
Holder2 holder2 = new Holder2(new Automobile());
//把object 强制转型为 Automobile
Automobile automobile = (Automobile) holder2.getObject();
//设置对象的属性
holder2.setObject("the object is Automobile");
//获取对象的属性
String string = (String) holder2.getObject();
holder2.setObject(1);
Integer a= (Integer) holder2.getObject();
}
}
- Holder2 可以存储任何类型的对象,在这个例子中,只用了一个Holder2对象,却先后存储了三种不同类型的对象 (Automobile , String ,Integer 三个对象)。
- 有些时候,我们确实希望容器能够同时持有多种类型的对象。但是,通常而言,我们只会使用容器来存储一种类型的对象。泛型的主要目的之一就是指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。
public class Holder3 <T>{
private T t;
public Holder3(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public static void main(String[] args) {
Holder3<Automobile> holder3 = new Holder3<Automobile>(new Automobile());
Automobile automobile = holder3.getT();
//如下会报错
//holder3.setT(1); setT(参数必须为 Automobile 类型)
}
}
- 当你创建Holder3 对象时,必须指明有什么类型的对象,将其置于尖括号内。就像 main() 中那样。然后,你就只能在 Holder3 中存入该类型(或其子类,因为多态与泛型不冲突)的对象了。
- 在你从 Holder3 中取出它持有的对象时,自动地就是正确的类型。
- 这就是Java 泛型的核心概念: 告诉编译器想使用什么类型,然后编译器帮你处理一切细节。
一个元组类库
- 仅一次方法调用就能返回多个对象,你应该经常需要这样的功能吧。可是 return 语句只允许返回单个对象,因此,解决办法就是创建一个对象,用它来持有想要返回的多个对象。
- 当然,可以在每次需要的时候,专门创建一个类来完成这样的工作。可是有了泛型,我们就能够一次性地解决该问题,以后再也不用在这个问题上浪费时间了。同时,我们再编译期就能确保类型安全。这个概念被称为 元组。
- 元组: 它是将一组对象直接打包存储于其中的一个单一对象。这个容器对象允许读取其中元素,但是不允许向其中存放新的对象。(这个概念也被称为 数据传送对象或信使)
//如下 是一个2维元组,它能够持有两个东西
public class TwoTuple<A,B> {
public final A a;
public final B b;
public TwoTuple(A a, B b) {
this.a = a;
this.b = b;
}
@Override
public String toString() {
return "TwoTuple{" +
"a=" + a +
", b=" + b +
'}';
}
}
- 构造器捕获了要存储对象,而toString() 是一个便利函数,用来显示列表中的值。注意,元组隐含地保持了其中元素的次序。
- 问题发现: 第一次阅读上面的代码时,你也许会想,这不是违反了 Java编程的安全性原则吗?
- a 和 b 应该声明为 private ,然后提供 getA() 和 getB() 之类的访问方法才对呀? 让我们仔细看看这个例子中的安全性: 客户端程序可以读取 a 和 b 对象,然后可以随心所欲地使用 这两个东西。但是 ,它们却无法将其他赋值 赋予 a和 b 。因为 final 声明 为你买了相同的安全保险,而且这种格式更简洁明了。
- 还有另一种设计考虑,即你确实希望允许客户端程序员改变 a 和 b 所引用的对象。然而, 采用以上的形式无疑是更安全的做法,这样的话,如果程序员想要使用具体具