Java中的泛型(附带实例)
泛型(Generics)是 Java 语言引入的一个非常重要的机制,它允许程序在编译阶段检查类型是否非法,进而可以避免类型转换失败的风险。
泛型一般应用于方法和类,被声明了泛型的方法和类可以不明确指定某个类型,它就好比模板,可以匹配多种类型。
我们先了解一下泛型标记符,这些标记符用于表示不同的类型,它能匹配传入的任意 Java 类型。常见的泛型标记符如下:
实际上 Java 并没有限定只能用以上这些符号作为泛型标记符,我们可以使用 A~Z 中的任意一个英文字母作为标记符,编译器也能编译成功。之所以提出以上五个标记符是为了增加代码的可读性,这只是一种开发约定。
下面来看泛型方法的语法,其实就是在普通方法中添加泛型标记符,同时参数列表的类型也要使用泛型标记符:
下面是泛型方法的 7 个示例:
如果需要,我们还可以在一个方法中声明多个泛型标记符。比如下面的两个示例,每个方法都同时声明了 T 和 E 两个泛型参数:
根据上述泛型方法的语法编写下面的实例:
注意对于基本数据类型,编译器会自动转换成对应的包装类,比如“10.11”会转换为 Double 类型,而“10”则会转换为 Integer 类型。
泛型类型的语法如下,其实就是在普通类的类名后面添加泛型标记符:
根据泛型类型语法我们来看下面的实例:
比如在下面的实例中定义一个 MyInterface<T> 泛型接口,里面包含了一个 test(T t)泛型方法,我们就可以在实现接口时指定类型,比如 MyInterface<String> 和 MyInterface<int[]>。
泛型一般应用于方法和类,被声明了泛型的方法和类可以不明确指定某个类型,它就好比模板,可以匹配多种类型。
我们先了解一下泛型标记符,这些标记符用于表示不同的类型,它能匹配传入的任意 Java 类型。常见的泛型标记符如下:
- E:即 Element,用于表示集合元素的类型;
- T:即 Type,用于表示常规类型;
- K:即 Key,用于表示键值对中键的类型;
- V:即 Value,用于表示键值对中值的类型;
- N:即 Number,用于表示数值类型。
实际上 Java 并没有限定只能用以上这些符号作为泛型标记符,我们可以使用 A~Z 中的任意一个英文字母作为标记符,编译器也能编译成功。之所以提出以上五个标记符是为了增加代码的可读性,这只是一种开发约定。
Java泛型方法
泛型方法是指一个方法具备了泛型的机制,它可以接收不同类型的参数,在调用时传入任意类型的参数,编译器能够根据不同类型的参数处理不同类型的方法调用。下面来看泛型方法的语法,其实就是在普通方法中添加泛型标记符,同时参数列表的类型也要使用泛型标记符:
访问修饰符 非访问修饰符 <泛型标记符> 返回类型 方法名(参数列表){ 方法体 }
下面是泛型方法的 7 个示例:
public <T> String join(T t) public <T> T join(T t) public <T> String join(T t,int num) public <T> String join(int num,T t) public <T> void join(T t) public static <T> String join(T t) public <E> void printArray(E[] arr)仔细观察它与普通方法之间的差异,其中的 T 和 E 就是泛型标记符,参数列表中的泛型参数不影响其他参数,顺序也可以根据需要确定。此外,方法的返回值类型也可以用泛型标记符表示。
如果需要,我们还可以在一个方法中声明多个泛型标记符。比如下面的两个示例,每个方法都同时声明了 T 和 E 两个泛型参数:
public <T,E> String join(T t,E e) public <T,E> String join(T t,E e,int num)
根据上述泛型方法的语法编写下面的实例:
public class GenericsTest { public static void main(String[] args) { print(10.11, "Double"); print(10, "Integer"); print("hello", "String"); } public static <E> void print(E e, String s) { if (s.equals("Double")) System.out.println("E is a Double type : " + e); if (s.equals("Integer")) System.out.println("E is a Integer type : " + e); if (s.equals("String")) System.out.println("E is a String type : " + e); } }输出结果为:
E is a Double type : 10.11
E is a Integer type : 10
E is a String type : hello
注意对于基本数据类型,编译器会自动转换成对应的包装类,比如“10.11”会转换为 Double 类型,而“10”则会转换为 Integer 类型。
Java泛型类型
泛型类型是指一个类具备了泛型的机制,它可以接收不同类型的参数,在创建对象时传入指定类型的参数。泛型类型的语法如下,其实就是在普通类的类名后面添加泛型标记符:
访问修饰符 class 类名<泛型标记符>{ }下面是泛型类型定义的例子,可以在类名后面添加 <E> 使类具备泛型机制,当然也支持多个类型参数,即用多个泛型标记符来表示,比如 <E,T,K,V>。
public class GenericsTest<E> public class GenericsTest<E,T,K,V>
根据泛型类型语法我们来看下面的实例:
public class GenericsTest { public static void main(String[] args) { String data2 = "hello world"; Info data3 = new Info("200", true); Result<String> r2 = new Result<String>(data2); Result<Info> r3 = new Result<Info>(data3); System.out.println(r2); System.out.println(r3); } } class Result<T> { T data; public Result(T t) { this.data = t; } public String toString() { return data.toString(); } } class Info { String code; boolean isSuccess; public Info(String s, boolean b) { this.code = s; this.isSuccess = b; } public String toString() { return code + "-" + isSuccess; } }输出结果为:
hello world
200-true
Java泛型接口
类似地,我们也可以定义泛型接口,它能让一个普通接口具有泛型的能力。比如在下面的实例中定义一个 MyInterface<T> 泛型接口,里面包含了一个 test(T t)泛型方法,我们就可以在实现接口时指定类型,比如 MyInterface<String> 和 MyInterface<int[]>。
public class GenericsTest { public static void main(String[] args) { new MyImplement().test("hello"); new MyImplement2().test(new int[] { 1, 2, 3, 4, 5 }); } } interface MyInterface<T> { public abstract void test(T t); } class MyImplement implements MyInterface<String> { public void test(String s) { System.out.println(s); } } class MyImplement2 implements MyInterface<int[]> { public void test(int[] arr) { System.out.println(Arrays.toString(arr)); } }输出结果为:
hello
[1, 2, 3, 4, 5]