目录
1. 概要
- Java泛型特点是一套代码,多次应用。其中多次特指适用多用参数类型。
- Java泛型体现在编译阶段,编译过后,泛型类型被擦除,替换为基本Object类型。
- Java泛型包括类泛型、接口泛型、方法泛型,其中方法泛型包括静态方法泛型(
不兼容类上的泛型参数
)。 - 不能创建指定参数类型的泛型数组,但可创建通配符类型的泛型数组。
- Java反射可以破坏泛型执行。
2. 一套代码,适配多种参数类型
以下是一段简单的代码示例,不再赘述。
public class GenericsDemo<T> {
public T g_var_a;
public void set(T data) {
g_var_a = data;
}
public T get() {
return g_var_a;
}
}
public class GenericsUsingDemo {
public static void main(String[] args) {
GenericsDemo<String> g_str = new GenericsDemo<String>();
g_str.set("test");
System.out.println(g_str.get());
GenericsDemo<Integer> g_int = new GenericsDemo<Integer>();
g_int.set(100);
System.out.println(g_int.get());
}
}
3. 编译过后,类型擦除
反编译上文GenericsUsingDemo类,可以查看调用GenericsDemo类的set方法参数类型和get方法返回类型均为java/lang/Object。使用时,调用checkcast指令自动进行类型转换。
public class GenericsUsingDemo {
public GenericsUsingDemo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class GenericsDemo
3: dup
4: invokespecial #3 // Method GenericsDemo."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String test
11: invokevirtual #5 // Method GenericsDemo.set:(Ljava/lang/Object;)V
14: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
17: aload_1
18: invokevirtual #7 // Method GenericsDemo.get:()Ljava/lang/Object;
21: checkcast #8 // class java/lang/String
24: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: new #2 // class GenericsDemo
30: dup
31: invokespecial #3 // Method GenericsDemo."<init>":()V
34: astore_2
35: aload_2
36: bipush 100
38: invokestatic #10 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
41: invokevirtual #5 // Method GenericsDemo.set:(Ljava/lang/Object;)V
44: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
47: aload_2
48: invokevirtual #7 // Method GenericsDemo.get:()Ljava/lang/Object;
51: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
54: return
}
4. 泛型类型
如前文所述,Java泛型包括类泛型、接口泛型、方法泛型。其中类泛型、接口泛型不再相赘述,本节重点探讨下方法泛型。
4.1 普通泛型方法
普通泛型方法定义需要在方法返回类型前指定泛型标志<T>,且可直接引用泛型类上的泛型标志。
public class GenericsDemo<T> {
public T g_var_a;
public T g_var_b;
public void set(T data) {
g_var_a = data;
}
public T get() {
return g_var_a;
}
public <M> void g_method_set(M m_data, T t_data) {
g_var_a = t_data;
System.out.println(g_var_a);
System.out.println(m_data);
}
}
4.2 静态泛型方法
静态泛型方法不能访问泛型类上的泛型标志。
public class GenericsDemo<T> {
public T g_var_a;
public T g_var_b;
public void set(T data) {
g_var_a = data;
}
public T get() {
return g_var_a;
}
public static <M> void g_method_set(M m_data, T t_data) {
g_var_a = t_data;
System.out.println(g_var_a);
System.out.println(m_data);
}
}
[root@iZbp1fza94gkbdpgoq00xcZ test]# javac GenericsDemo.java
GenericsDemo.java:17: error: non-static type variable T cannot be referenced from a static context
public static <M> void g_method_set(M m_data, T t_data) {
^
GenericsDemo.java:18: error: non-static variable g_var_a cannot be referenced from a static context
g_var_a = t_data;
^
GenericsDemo.java:19: error: non-static variable g_var_a cannot be referenced from a static context
System.out.println(g_var_a);
^
3 errors
4.3 可变参数泛型方法
如果用泛型标志悠久可变参数,则调用该方法,可传入任意类型参数组合。
public class GenericsDemo<T> {
public T g_var_a;
public T g_var_b;
public void set(T data) {
g_var_a = data;
}
public T get() {
return g_var_a;
}
public <M> void g_method_set(M... m_data) {
for(M m : m_data) {
System.out.println(m);
}
}
}
public class GenericsUsingDemo {
public static void main(String[] args) {
GenericsDemo<String> g_str = new GenericsDemo<String>();
g_str.g_method_set("test", 1);
}
}
public class GenericsUsingDemo {
public static void main(String[] args) {
GenericsDemo<?> g_obj = new GenericsDemo<Object>();
g_obj.g_method_set("test", 1);
}
}
[root@iZbp1fza94gkbdpgoq00xcZ test]# java GenericsUsingDemo
test
1
5. 泛型数组
声明泛型数组须使用通配符标志,不能指定参数类型。具体原因,可以仔细思考下前文反编译中的checkcast指令。
public class GenericsUsingDemo {
public static void main(String[] args) {
GenericsDemo<?>[] g_obj = new GenericsDemo<?>[5];
System.out.println(g_obj.length);
}
}
另外,虽然可以声明通配符类型数组,但不能声明通配符类型变量。
[root@iZbp1fza94gkbdpgoq00xcZ test]# javac GenericsUsingDemo.java
GenericsUsingDemo.java:7: error: unexpected type
GenericsDemo<?> g_obj = new GenericsDemo<?>();
^
required: class or interface without bounds
found: ?
1 error
6. 泛型上下边界
泛型还可以应用上下边界,语法如下,不再赘述。
- <? extends T>:是指 “上界通配符(Upper Bounds Wildcards)
- <? super T> :是指 “下界通配符(Lower Bounds Wildcards)
7. Java反射对泛型的影响
Java反射可以影响泛型的执行阶段。如下面一段代码,可以通过编译阶段,但在执行时会提示Integer类型无法转换为String类型异常。如果不使用反射,如前文示例,直接调用set方法,传递Integer类型参数,会在编译阶段报错。
1 import java.lang.reflect.*;
2
3 public class GenericsUsingDemo {
4
5
6 public static void main(String[] args) throws Exception {
7 GenericsDemo<String> g_obj = new GenericsDemo<String>();
8 Class<?> clazz = g_obj.getClass();
9 Method method = clazz.getMethod("set", Object.class);
10 method.invoke(g_obj, 1);
11 System.out.println(g_obj.get());
12
13
14 }
15
16 }
[root@iZbp1fza94gkbdpgoq00xcZ test]# javac GenericsUsingDemo.java
[root@iZbp1fza94gkbdpgoq00xcZ test]# java GenericsUsingDemo
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at GenericsUsingDemo.main(GenericsUsingDemo.java:11)