Java泛型01:基础知识

1. 泛型程序设计

泛型是Java程序设计中一个重要的思想,它可以被用在类、接口、方法中。泛型简单来说就是:

1)所编写的代码在不用修改的前提下,可以被多种不同类型的对象所重用。

2)相较于杂乱的使用Object变量,泛型机制编写的程序具有更好的安全性和可读性,在效率上也会有所提升。

Java在JDK 1.5 之后增加了泛型机制,我们所熟知的 ArrayList 类就是泛型程序设计的一个典型例子。

1.1 使用泛型类的例子:ArrayList

ArrayList<String> stringList = new ArrayList<String>();

上述代码中的 String 就是类型参数,它指明了 ArrayList 中存储的数据类型为 String 类型。也就是说,对于 stringList 而言,我们只能往里面添加 String 类型的对象。

	String str = "";
	Integer a = 0;
	stringList.add(str); // ok
	stringList.add(a); // error

如上述例子所示,当我们向 stringList 添加其它类型的对象时,就会产生错误。

需要注意的是,类型参数不能是八大基本类型,如果的确需要用到它们,可以用其包装类型替代(如用 Integer 代替int)。

JDK 1.7 之后,构造函数中的类型参数可以被省略,省略的类型可以从变量的类型推断得出:

1.2 类型变量

在构建泛型类、泛型接口和泛型方法时,可以通过类型变量来“泛型化”数据。例如:

public class Pair<T> {
}

Pair<T> 中的 T 就是类型变量,被放在泛型类类名后面的尖括号里。

关于类型变量,在Java库中,常使用E、K、V、T等大写字母表示:
E: 表示集合的元素类型
K:表示关键字
V:表示值(常与K对应)
T:表示“任意类型”(需要时还可以用邻近的字母U和S)

类型变量可以定义数据域的类型、方法的参数类型以及返回值的类型。

2. 泛型类

利用类型变量,可以构建自己的泛型类。

public class Pair<T> {
	private T first; 
	private T second;
	
	public Pair() {
		first = null;
		second = null;
	} 
	
	public Pair(T first, T second) {
		this.first = first;
		this.second = second;
	}
	
	public T getFirst() {
		return first;
	} 
	
	public T getSecond() {
		return second;
	}
	
	public void setFirst (T newValue) {
		first = newValue;
	} 
	
	public void setSecond(T newValue) {
		second = newValue;
	}
	
}

泛型类 Pair 利用类型变量 T 定义了数据域的类型、方法的参数类型以及返回值的类型。在使用到Pair类时,只需要在实例化对象时用具体的类型参数替换类型变量即可。

泛型的实现利用了Java虚拟机的类型擦除机制。为便于理解,可以将类型变量T理解成“某种变量”,在实例化泛型类时需要具体指明其类型,并用指定的类型“替换”它,以得到一个“具体的”对象。
3. 泛型接口

同样的,我们可以构建自己的泛型接口。这里需要注意的是, 接口中的数据域都是public static final(静态常量),因此不能用类型变量定义。

这并不难理解,对于静态常量而言,它的值在类或接口建立的时候就是确定的,既然如此,它肯定不能是个“类型不确定”的变量(类型都不能确定,它的值还怎么定下来呢?)。
 

public interface Person<T> {
	T information; // error

	T getInformation(); // ok
}

对于泛型接口的实现,可以在实现时就指定类型变量的具体类型,这样实现的类就是一个具体的类,而不是泛型类,如:

public class MyPerson implements Person<String> {
	String information;
	
	@Override
	public String getInformation() {
		return information;
	}
	
}

当然也可以在实现此接口时不具体指定类型变量的具体类型,这样得到的就是一个泛型类,如:

public class MyPerson<T> implements Person<T> {
	T information;

	@Override
	public T getInformation() {
		return information;
	}
	
}

4. 泛型方法

除了泛型类和泛型接口,实际上,还可以在普通类中定义泛型方法(带有类型参数的方法),例如:

public class MyPerson {
	
	public static <T> T getFirstInformation(T[] information) {
		if (information.length >= 2) {
			return information[1];
		}
		return null;
	}
	
}

从尖括号和类型变量可以看出,这是一个泛型方法。当调用泛型方法时,需要在方法名之前的尖括号中填入具体的类型,如:

	String[] information = {"111111", "ABCDD", "boy"};
	String name = MyPerson.<String>getFirstInformation(information);
	System.out.println(name);

	// 程序打印:ABCDD

需要注意的是,在定义时尖括号被放在了修饰符之后,返回类型之前;在调用时尖括号被放在了方法名之前。这样定义的好处是可以避免语法分析的歧义。

试想如果尖括号被放在方法名之后,如方法: f<T, U>();
在调用方法时,对于g(f<a,b>(c)),是理解成函数f<a,b>(c)的返回值,还是两个布尔变量f<a和b>(c)呢?

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章