泛型概述
泛型程序设计
泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。——《百度百科》
Java泛型
Java 泛型的参数只可以代表类,不能代表个别对象。由于Java泛型的类型参数之实际类型在编译时会被消除,所以无法在运行时得知其类型参数的类型,而且无法直接使用基本值类型作为泛型类型参数。——《百度百科》
使用泛型的好处
- 编译期进行类型检查,消除运行期ClassCastException异常;
- 消除了强制类型转换,减少不必要的代码。
泛型类型
在类/接口的定义中含有类型参数,类型参数在整个类/接口中有效。
泛型类(接口)
示例的泛型类中包含一个类型参数T,T可以接收任意引用变量类型。
泛型接口与泛型类相似。
/** 泛型类 */
public class GenericClass<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public static void main(String[] args) {
GenericClass<String> genericString = new GenericClass<>();
genericString.setT("a string");
String resString = genericString.getT();
System.out.println("泛型<String> , t is : " + resString);
GenericClass<Integer> genericInteger = new GenericClass<>();
genericInteger.setT(1);
// 自动拆箱
int resInt = genericInteger.getT();
System.out.println("泛型<Integer> , t is : " + resInt);
}
}
泛型的命名规范
泛型类型使用单个大写字母命名,比如T。以下列出一些常用的类型参数:
- E : Element (used extensively by the Java Collections Framework)
- K : Key
- N : Number
- T : Type
- V : Value
- S,U,V etc. : 2nd, 3rd, 4th types
多种类型参数
示例的类中含有2个类型参数K,V。
/**
* 两个类型参数的泛型示例
*
* @param <K> key
* @param <V> value
*/
public class MyMap<K, V> {
private K k;
private V v;
public MyMap(K k, V v) {
this.k = k;
this.v = v;
}
public V getV(K k) {
return v;
}
public static void main(String[] args) {
MyMap<String, String> myStringMap = new MyMap<>("key", "value");
String vString = myStringMap.getV("key");
System.out.println(vString);
MyMap<Integer, Integer> myIntMap = new MyMap<>(1, 1);
// unboxing
int vInt = myIntMap.getV(1);
System.out.println(vInt);
}
}
泛型方法
在方法的定义中含有类型参数,类型参数在整个方法中有效。
静态方法、非静态方法、构造方法中都允许含有类型参数。
/** 在方法中使用泛型 */
public class GenericMethod {
/**
* 非静态方法
*
* @param t
* @param <T>
* @return
*/
public <T> T nonStaticGet(T t) {
return t;
}
/**
* 静态方法
*
* @param t
* @param <T>
* @return
*/
public static <T> T staticGet(T t) {
return t;
}
/**
* 多类型参数
*
* @param key
* @param value
* @param <K>
* @param <V>
* @return
*/
public <K, V> String put(K key, V value) {
// 存储key、value...
return "key : " + key + " , value : " + value;
}
public static void main(String[] args) {
String str = GenericMethod.staticGet("static method!");
System.out.println(str);
GenericMethod gm = new GenericMethod();
str = gm.nonStaticGet("non-static method!");
System.out.println(str);
str = gm.put(1, "one");
System.out.println(String.format("Multiple Type Parameters! %s", str));
}
}
有界类型参数
- 使用类型参数T时,对传入的参数是没有任何限制;我们可以使用extends关键字实现对类型参数的限制,比如:T extends Number,T只允许传入数字类型的参数;extends关键字后面可以是类或者接口;
- extends后面可以有多个类型限制,比如:<T extends A & B & C>。注意如果A、B、C中有一个是类,则类必须被放置在第一个位置,如例子中的A。
public class BoundedTypeParameters {
public static <T extends Number> void print(T t) {
System.out.println("The number is " + t);
}
public <T extends A & B & C> void multipleBounds(T t) {
/* ... */
}
/**
* 编译出错 —— 类必须在第一个位置
*
* @param t
* @param <T>
*/
// public <T extends B & A & C> void multipleBounds1(T t) {}
public static void main(String[] args) {
BoundedTypeParameters.print(1);
BoundedTypeParameters.print(1.1D);
// 编译错误
// BoundedTypeParameters.print("1");
}
class A {
/* ... */
}
interface B {
/* ... */
}
interface C {
/* ... */
}
}
通配符
通配符表示未知类型,在泛型中用?表示通配符,例如:List<?>。
上界通配符
使用extends关键字,例如<? extends Number>,类型参数只能是Number本身或Number的子类。
下届通配符
使用super关键字,例如<? super Integer>,类型参数只能是Integer本身或Integer的父类。
/** 泛型通配符例子 */
public class Wildcards {
/**
* 无界通配符
*
* @param list
*/
public void print(List<?> list) {
for (Object o : list) {
System.out.print(o + " ");
}
System.out.println();
}
public void printUpperBounded(List<? extends Number> list) {
for (Object o : list) {
System.out.print(o + " ");
}
System.out.println();
}
public void printLowerBounded(List<? super Integer> list) {
for (Object o : list) {
System.out.print(o + " ");
}
System.out.println();
}
public static void main(String[] args) {
Wildcards wildcards = new Wildcards();
List<Integer> intList = Arrays.asList(1, 2);
wildcards.print(intList);
wildcards.printUpperBounded(intList);
List<String> strList = Arrays.asList("1", "2");
// 编译出错,类型参数只能是Number本身或Number的子类,如:List<Integer>
// wildcards.printUpperBounded(strList);
List<Object> objList = Arrays.asList("Hello", "World!");
wildcards.printLowerBounded(objList);
// 编译出错,类型参数只能是Integer本身或Integer的父类,如:List<Object>
// wildcards.printLowerBounded(strList);
}
}