问题引入
第一次在《Java编程思想》中接触到泛型这个概念是在第十一章中,这一章讲的是持有对象。
首先来看一份没有使用泛型的代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import java.util.ArrayList;
class Apple {
private static long counter;
private final long id = counter++;
public long id() { return id; }
}
class Orange {}
public class ApplesAndOrangesWithoutGenerics {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ArrayList apples = new ArrayList();
for (int i = 0; i < 3; i++)
apples.add(new Apple());
apples.add(new Orange());
for(int i = 0; i < apples.size(); i++)
((Apple)apples.get(i)).id();
}
}
代码分析:
首先定义两个类(Apple和Orange),两个类除了都默认继承自Object类之外没有任何共性。
主函数中尝试将三个Apple对象和一个Orange对象放入容器ArrayList apples
中,使用了@SuppressWarnings注解及其参数表示只有有关“不受检查的异常”的警告信息应该被抑制。
最后一个for循环用于获取容器apples中的对象的id。
运行结果:
报错信息holding.Orange cannot be cast to holding.Apple
,Orange类型不能转型为Apple类型
然后来看一份使用了泛型的代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14import java.util.ArrayList;
public class ApplesAndOrangesWithGenerics {
public static void main(String[] args) {
ArrayList<Apple> apples = new ArrayList<Apple>();
for (int i = 0; i < 3; i++)
apples.add(new Apple());
// apples.add(new Orange());
for (int i = 0; i < apples.size(); i++)
System.out.println(apples.get(i));
for (Apple c : apples)
System.out.println(c.id());
}
}
代码分析:
执行的操作和之前的代码一样,区别在于初始化容器时ArrayList
后面加了一个<Apple>
,尖括号括起来的是类型参数(可以有多个),它指定了这个容器实例可以保存的类型。在此处我将添加Orange对象到容器注释掉了,因为它在编译期就会报错,而上一份代码在运行时才能看出错误。
个人理解(2019-10-21)
因为目前还没有对泛型进行深入的学习,所以仅仅通过上面的代码我个人对泛型的理解类似于方法的形参,编译期就规定好了容器内对象的类型,这样可以减少运行时错误(变成编译期错误)。并且注意到在将元素从List中取出时,类型转换也不再是必须的了。因为List知道它保存的是什么类型,因此它会在调用get()时替你执行转型。这样,通过使用泛型,你不仅知道编译器将会检查你放置到容器中的对象类型,而且在使用容器中的对象时,可以使用更加清晰的语法。
后续深入学习泛型后再做补充。
Q. Do you know Generics? How did you used in your coding?
Generics
allows type (Integer, String, … etc and user defined types) to be a parameter to methods, classes and interfaces. For example, classes like HashSet, ArrayList, HashMap, etc use generics very well.
Advantages
- Type-safety: We can hold only a single type of objects in generics. It doesn’t allow to store other objects.
- Type Casting: There is no need to typecast the object.
- Compile-Time Checking: It is checked at compile time so problem will not occur at runtime.
Example:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33/**
* A Simple Java program to show multiple
* type parameters in Java Generics
*
* We use < > to specify Parameter type
*
**/
class GenericClass<T, U> {
T obj1; // An object of type T
U obj2; // An object of type U
// constructor
GenericClass(T obj1, U obj2) {
this.obj1 = obj1;
this.obj2 = obj2;
}
// To print objects of T and U
public void print() {
System.out.println(obj1);
System.out.println(obj2);
}
}
// Driver class to test above
class MainClass {
public static void main (String[] args) {
GenericClass <String, Integer> obj =
new GenericClass<String, Integer>("Generic Class Example !", 100);
obj.print();
}
}
Output:1
2Generic Class Example !
100