泛型(Generic)
泛型的引入
问题:我们之前实现过的顺序表,只能保存 int 类型的元素,如果现在需要保存 指向 Person 类型对象的引用
的顺序表,请问应该如何解决?如果又需要保存指向 Book 对象类型的引用呢?
回答:
- 首先,我们在学习多态过程中已知一个前提,基类的引用可以指向子类的对象。
- 其次,我们也已知 Object 是 java 中所有类的祖先类。
那么,要解决上述问题,我们很自然的想到一个解决办法,将我们的顺序表的元素类型定义成 Object 类型,这
样我们的object 类型 的引用可以指向Person 类型的对象或 指向 Book 类型的对象
public class MyArrayList {
private Object[] array;
private int size;
public void add(Object 0){尾插}
public Object get(int index){获取index 位置的元素}
MyArrayList books = new MyArrayList();
for (int i = 0; i < 10; i++) {
books.add(new Book());
// 尾插 10 本书到顺序表
}
MyArrayList people = new MyArrayList();
for (int i = 0; i < 10; i++) {
people.add(new Person());
// 尾插 10 个人到顺序表
}
}
遗留问题:现在的 MyArrayList 虽然可以做到添加任意类型的引用到其中了,但遇到以下代码就会产生问题。
提示:问题暴露的越早,影响越小。编译期间的问题只会让开发者感觉到,运行期间的错误会让所有的软件使用者
承受错误风险。
所以我们需要一种机制,可以 1. 增加编译期间的类型检查 2. 取消类型转换的使用 泛型就此诞生!
泛型的分类
泛型类的定义的简单演示
//. 尖括号 <> 是泛型的标志
// 2. E 是类型变量(Type Variable),变量名一般要大写
// 3. E 在定义时是形参,代表的意思是 MyArrayList 最终传入的类型,但现在还不知道
public class MyArrayList<E> {
private E[] array;
private int size;
...
}
一、泛型背后作用时期和背后的简单原理
- 泛型是作用在编译期间的一种机制,即运行期间没有泛型的概念。
- 泛型代码在运行期间,就是我们上面提到的,利用 Object 达到的效果
二、泛型类的使用
// 定义了一个元素是 Book 引用的
MyArrayList MyArrayList<Book> books = new MyArrayList<Book>();
books.add(new Book());
// 会产生编译错误,Person 类型无法转换为 Book 类型
books.add(new Person());
// 不需要做类型转换
Book book = book.get(0);
// 不需要做类型转换
// 会产生编译错误,Book 类型无法转换为 Person 类型
Person person = book.get(0);
通过以上代码,我们可以看到泛型类的一个使用方式:只需要在所有类型后边跟尖括号,并且尖括号内是真正的类
型,即 E 可以看作的最后的类型
泛型总结
- 泛型是为了解决某些容器、算法等代码的通用性而引入,并且能在编译期间做类型检查。
- 泛型利用的是 Object 是所有类的祖先类,并且父类的引用可以指向子类对象的特定而工作。
- 泛型是一种编译期间的机制,即 MyArrayList 和 MyArrayList 在运行期间是一个类型。
- 泛型是java 的一种合法的 语法 标志是<>
包装类
包装类的使用
装箱 和 拆箱
int i = 10;
// 装箱操作,新建一个 Integer 类型对象,将 i 的值放入对象的某个属性中
Integer ii = Integer.valueOf(i);
Integer ij = new Integer(i);
// 拆箱操作,将 Integer 对象中的值取出,放到一个基本数据类型中
int j = ii.intValue();
-
java 提供了自动机制。
- 自动装箱和自动拆箱 是工作在编译期间的一种机制
javap 反编译工具
javap -c 类名称
Compiled from "Main.java"
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 10
2: istore_1
3: iload_1
4: invokestatic #2 //Methodjava/lang/Integer.valueOf:
(I)Ljava/lang/Integer;
7: astore_2
8: iload_1
9: invokestatic #2
// Method java/lang/Integer.valueOf:
(I)Ljava/lang/Integer;
12: astore_3
13: aload_2
14: invokevirtual #3
// Method java/lang/Integer.intValue:()I
17: istore 4
19: aload_2
20: invokevirtual #3
// Method java/lang/Integer.intValue:()I
23: istore 5
25: return
}
List
、
常见方法
import java.util.LinkedList;
public class ListDemo {
public static void main(String[] args) {
List<String> courses = new ArrayList<>();
courses.add("C 语言");
courses.add("Java SE");
courses.add("Java Web");
courses.add("Java EE");
// 和数组一样,允许添加重复元素
courses.add("C 语言");
// 按照添加顺序打印
System.out.println(courses);
// 类似数组下标的方式访问
System.out.println(courses.get(0));
System.out.println(courses);
courses.set(0, "计算机基础");
System.out.println(courses);
// 截取部分 [1, 3)
List<String> subCourses = courses.subList(1, 3); System.out.println(subCourses);
// 重新构造
List<String> courses2 = new ArrayList<>(courses);
System.out.println(courses2);
List<String> courses3 = new LinkedList<>(courses); System.out.println(courses3);
// 引用的转换
ArrayList<String> courses4 =(ArrayList<String>)courses2;
System.out.println(courses4);
// LinkedList<String> c = (LinkedList<String>)course2; 错误的类型
LinkedList<String> courses5 = (LinkedList<String>)courses3; System.out.println(courses5);
// ArrayList<String> c = (ArrayList<String>)course3; 错误的类型