Java:泛型

问题引入

第一次在《Java编程思想》中接触到泛型这个概念是在第十一章中,这一章讲的是持有对象。
首先来看一份没有使用泛型的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import 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
14
import 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
2
Generic Class Example !
100