Java泛型
Java泛型
- 自己写一个ArrayList类,对比不使用泛型和使用泛型的区别,观察Java内部库中ArrayList类是如何使用泛型的;
- 在使用泛型的时候同时可以进行向上转型,但要避免一些问题;
- 定义一个泛型包括在类中定义泛型,静态方法中使用泛型,定义一个多类型的泛型,在接口中定义泛型;使用泛型和不使用泛型的区别
1. ArrayList 泛型
- 不使用泛型会可能会导致强制类型转换错误
- 使用泛型可以避免强制类型转换方法
- 其实Java内部库中ArrayList类已经使用了泛型
定义一个ArrayList泛型
1
2
3
4
5
6
7public class ArrayList {
private Object[] array;
private int size;
public void add(Object e) {...}
public void remove(int index) {...}
public Object get(int index) {...}
}表现在使用的时候
1
2
3
4
5
6
7
8
9
10// 如果不使用泛型,下面是声明的方式
ArrayList list = new ArrayList();
// 存入的是字符串,但是没有指定就是Object类型
list.add("Hello");
// 获取到Object,必须强制转型为String:
String first = (String) list.get(0);
// 传入的是Integer类型
list.add(new Integer(123));
// ERROR: ClassCastException:
String second = (String) list.get(1);如果不使用泛型,那么就需要建立 StringArrayList\IntegerArrayList 等等类
- 解决方法使用泛型
1
2
3
4
5
6
7
8//这是定义泛型的基本格式
public class ArrayList<T> {
private T[] array;
private int size;
public void add(T e) {...}
public void remove(int index) {...}
public T get(int index) {...}
}1
2
3
4
5
6// 如果使用泛型,下面是声明的方式
ArrayList<String> list = new ArrayList();
// 存入的是字符串,
list.add("Hello");
// 直接获取即可:
String first = list.get(0);
- Java内部库中的ArrayList已经实现了泛型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
.io.Serial
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
2. 向上转型
- 向上转型
- ArrayList
转型为 ArrayList 存在问题
在Java标准库中的
ArrayList<T>
实现了List<T>
接口,它可以向上转型为List<T>
:1
2
3
4
5public class ArrayList<T> implements List<T> {
...
}
List<String> list = new ArrayList<String>();
编译器不允许ArrayList
转型为
ArrayList这种转型,因为要避免如下错误 1
2
3
4
5
6
7
8
9
10
11
12// 创建ArrayList<Integer>类型:
ArrayList<Integer> integerList = new ArrayList<Integer>();
// 添加一个Integer:
integerList.add(new Integer(123));
// “向上转型”为ArrayList<Number>:
// 此时已经报错,原因这里赋值的意思:第一,这两个是同一个对象,类似于指针;第二,强制转换
// 即 numberList 和 integerList 同时指向一个对象
ArrayList<Number> numberList = integerList;
// 用 numberList 添加一个Float,因为Float也是Number;
numberList.add(new Float(12.34));
// 用 integerList 获取索引为1的元素:此时很明显回到类型转换错误,integer 类型不可能接受float类型
Integer n = integerList.get(1); // ClassCastException!- 根据继承关系理解,Float 类型和 Integer 类型都是 Number类的子类,尽管 Integer 向上转换成 Number 类型后接受了一个 Float 类型,但在获取的时候 Integer 类型是不能接受 Float 类型的
3. 定义和使用泛型
- 类定义泛型的基本步骤
- 静态方法中如何使用泛型
3.1 类与泛型
- 类定义泛型的基本步骤
- 先写一个普通的类
- 将所有的强类型地方换成T
- 类声明泛型
1 | public class Pair { |
3.2 静态方法与泛型
- 静态方法中的使用泛型注意点
- 不要和带有泛型的类混淆
1 | public class Pair<T> { |
3.3 多类型泛型
1 | // 定义 |
3.2 泛型与接口
- 对字符串String进行排序,能直接使用 sort方法因为 String类已经实现对应的泛型接口
- 自定义类进行排序必须实现对应的泛型接口
- 可以在接口中定义泛型
字符串排序
1 | String[] ss = new String[] { "Orange", "Apple", "Pear" }; |
自定义类进行排序
1 | // 对自定义的类进行排序 |
在接口中定义泛型,Comparable为例
1 | public interface Comparable<T> { |
3.5 使用泛型的区别
- 基本泛型的使用
- 不指定泛型,默认就是Object
- 若指定泛型,那么就是自动识别为强类型
基本使用
使用时,不指定泛型类型,那么就是 Object
1
2
3
4
5
6// 编译器警告:
List list = new ArrayList();
list.add("Hello");
list.add("World");
String first = (String) list.get(0);
String second = (String) list.get(1);指定泛型,那么就会自动识别为强类型
1
2
3
4
5
6
7// 无编译器警告:
List<String> list = new ArrayList<String>();
list.add("Hello");
list.add("World");
// 无强制转型:
String first = list.get(0);
String second = list.get(1);
未完待续