Java泛型是将类型由原来的具体的类型参数化,然后在使用时传入具体的类型。编译器在编译时会去掉类、接口或方法中类型参数的定义,目的是使得用了泛型的Java应用程序能够与未用泛型时创建的类库和应用程序保持兼容性。因此,需要知道类型才能执行的运算,如new运算、instanceof运算等,都无法执行。
定义泛型类型
定义泛型类型时用“< >”包含一个类型参数,类型参数常用单个的大写字母表示,常用的有:
T -> 类型
E -> 元素
K -> 关键字
根据泛型定义位置的不同,可以分为:
泛型类
泛型类是将类型参数定义在类名后:
1 class A<T> { 2 3 public void test(T t) { 4 System.out.println(t); 5 } 6 7 }
继承泛型类的类可以不指定泛型类型:
1 class B<T> extends A<T> { 2 3 @Override 4 public void test(T t) { 5 super.test(t); 6 } 7 8 }
也可以指定泛型类型:
1 class C extends A<String> { 2 3 @Override 4 public void test(String s) { 5 super.test(s); 6 } 7 8 }
创建泛型类实例时指定泛型类型:
1 @Test 2 void testGenericsClass() { 3 // 指定泛型类型为java.lang.Integer 4 B<Integer> b = new B<Integer>(); 5 b.test(1); 6 // b.test("1"); // 报错,传入参数只能为Integer类型 7 8 // 定义类时已经指定泛型类型为java.lang.String 9 C c = new C(); 10 c.test("1"); 11 }
输出结果:
泛型接口
泛型接口是将类型参数定义在接口名后。与泛型类相似,实现泛型接口的类可以指定或不指定泛型类型。
泛型方法
泛型方法时将类型参数定义在返回值类型前。
1 class D { 2 3 public<T> void test(T t) { 4 System.out.println(t); 5 } 6 7 }
调用同一个对象的泛型方法可以传入不同的泛型类型。这是泛型方法比泛型类实用的一点。
调用泛型方法可以在方法名前加上泛型类型。如果泛型方法参数中使用了泛型,则可以省略前面指定的泛型类型,编译器会根据参数类型自动匹配。
1 @Test 2 void testGenericsMethod() { 3 D d = new D(); 4 // 指定泛型类型为java.lang.String,传入的参数只能为String类型 5 d.<String>test("1"); 6 // 不指定泛型类型,编译器根据参数“1”自动识别泛型类型为java.lang.Integer 7 d.test(1); 8 }
输出结果:
限界类型参数
限界类型参数是限定可以取代类型参数的实际类型的范围,格式为:<类型参数 extends 类>,表示传入的泛型类型必须为extends指定的类或其子类。
1 class E<T extends Number> { 2 3 public void test(T t) { 4 System.out.println(t); 5 } 6 7 }
1 @Test 2 void testGenericsBoundedTypeParameter() { 3 E<Integer> e = new E<Integer>(); 4 // E<String> e = new E<String>(); // 报错,java.lang.String类不是java.lang.Number类的子类 5 }
通配符
泛型的通配符用“?”表示,代表未知类型,分为三种:
无界通配符(<?>):代表任意类型。
限界通配符(<? extends T>):代表T类或T的某个子类。
下界通配符(<? super T>):代表T类或T的某个父类。
1 @Test 2 void testGenericsWildcard() { 3 // 无界通配符 4 A<?> a1; 5 a1 = new A<Integer>(); 6 a1 = new A<String>(); 7 8 // 限界通配符 9 A<? extends Number> a2; 10 a2 = new A<Integer>(); 11 // a2 = new A<String>(); // 报错,java.lang.String类不是java.lang.Number类的子类 12 13 // 下界通配符 14 A<? super Integer> a3; 15 a3 = new A<Number>(); 16 // a3 = new A<String>(); // 报错,java.lang.String类不是java.lang.Integer类的父类 17 }