为什么需要泛型
public class MyArray {
private int[] arr = null;
private int size;
private int capacity;
public MyArray(int capacity) {
arr = new int[capacity];
size = 0;
this.capacity = capacity;
}
public void add(int index, int data) {
if(size < capacity) {
arr[size] = data;
size++;
}
}
public int get(int index) {
return arr[index];
}
public int size() {
return size;
}
public static void main(String[] args) {
MyArray m = new MyArray(10);
m.add(0,1);
m.add(1,2);
m.add(2,3);
m.add(3,4);
for(int i = 0; i < m.size(); i++) {
System.out.print(m.get(i) + " ");
}
System.out.println();
}
MyArray m1 = new MyArray(10);
m1.add(0,1.0);
}
通过上述示例发现,MyArray类中实际只能保存int类型的数据,对于其他类型的数据比如:double、String或者自定义类型的对象,根本无法存储。
想要解决上述问题,最简单的方式就是:对于不同的类型,分别实现各自的MyArray类即可,但是这个方式实现有点麻烦。
业界有大佬是按照如下方式改进的:将上述代码中存储数据的类型全部有int改为Object,因为在Java中Object是所有类的基类。
package xsk;
class Person {
String name;
Person(String name) {
this.name = name;
}
}
public class MyArray {
private Object[] arr = null;
private int size;
private int capacity;
public MyArray(int capacity) {
arr = new Object[capacity];
size = 0;
this.capacity = capacity;
}
public void add(Object data) {
if(size < capacity) {
arr[size] = data;
size++;
}
}
public Object get(int index) {
return arr[index];
}
public int size() {
return size;
}
public static void main(String[] args) {
MyArray m = new MyArray(10);
m.add(1);
m.add(2);
m.add(3);
m.add(4);
MyArray m1 = new MyArray(10);
m1.add(new Person("kin"));
m1.add(new Person("Linda"));
m1.add(new Person("jack"));
}
}
经改过之后的MyArray终于任意类型都可以存储了,最后:代码只需实现一份,但是任意类型都可以存储,貌似一切都比较美好。
但是大家使用之后,纷纷吐槽:因为Object是所有类的基类,那就意味着可以再一个MyArray中存储不同种类的数据类型喽
package xsk;
class Person {
String name;
Person(String name) {
this.name = name;
}
}
public class MyArray {
private Object[] arr = null;
private int size;
private int capacity;
public MyArray(int capacity) {
arr = new Object[capacity];
size = 0;
this.capacity = capacity;
}
public void add(Object data) {
if(size < capacity) {
arr[size] = data;
size++;
}
}
public Object get(int index) {
return arr[index];
}
public int size() {
return size;
}
public static void main(String[] args) {
MyArray m = new MyArray(10);
m.add(1);
m.add("Peter");
m.add(new Person("Kin"));
for(int i = 0; i < m.size(); i++) {
String s = (String)m.get(i);
System.out.println(s + " ");
}
}
}

运行时出错的原因非常简单:上述代码中,由于MyArray存储数据不都全是String类型的,那如果强转成String类型之后,肯定会发生类型转换异常。
以上就是JDK1.5之前的解决方式,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要作显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以在预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患,为了解决该问题,JDK1.5中引入了泛型。
泛型的概念
泛型是java1.5中增加的一个新特性,通过泛型可以写与类型无关的代码,即编写的代码可以被很多不同类型的对象所重用,经常用在一些通用的代码实现中,比如:java集合框架中的类几乎都是用泛型实现的。
泛型的本质是:类型参数化。类似函数传参一样样,传递不同的实参,函数运行完将会产生不同的结果。
泛型的分类
泛型主要包含:泛型类、泛型方法和泛型接口
泛型的定义
class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> {
// 类实现体
}
了解: 【规范】类型形参一般使用一个大写字母表示,常用的名称有:
- E 表示 Element
- K 表示 Key
- V 表示 Value
- N 表示 Number
- T 表示 Type
- S, U, V 等等 - 第二、第三、第四个个类型
泛型的例子
public class MyArray<E> {
private E[] arr = null;
private int size;
private int capacity;
public MyArray(int capacity) {
arr = (E[]) new Object[capacity];
size = 0;
this.capacity = capacity;
}
public void add(E data) {
if(size < capacity) {
arr[size] = data;
size++;
}
}
public E get(int index) {
return arr[index];
}
public int size() {
return size;
}
泛型的实例化
泛型类<类型实参> 变量名; 定义一个泛型类引用。 new 泛型类<类型实参>(构造方法实参); 实例化一个泛型类对象。
public static void main(String[] args) {
// 将泛型类使用String类型来实例化,表明m中只能存放String类型的对象
MyArray<String> m = new MyArray<>(10);
m.add("Peter");
m.add("David");
m.add("Jim");
// 编译失败:因为在实例化时,已经明确其内部只能存储String类型的对象
// Person对象和String对象之间不能转换
// m.add(new Person("Lily"));
for(int i = 0; i < m.size(); ++i){
// 此处从m中获取到的成员,再不需要进行强制类型转换了
String s = m.get(i);
System.out.print(s + " ");
}
}
注意:
右侧 <.> 中的类型可以省略
MyArray<String> m = new MyArray<>(10);
左侧 <.> 中的类型不能省略
MyArray<> m = new MyArray<String>(10); // 会编译失败
编译器在编译时,是通过左侧内容来推测右侧。
虽然右侧 < > 可以不用写类型,但是 < >不能省略
MyArray<String> m = new MyArray(10); // 会产生编译警告,自己永远不要这么用