Java基础[8]泛型程序设计
来源:互联网 发布:逆行武侠 知乎 编辑:程序博客网 时间:2024/06/10 13:01
1 使用泛型的好处
- 泛型设计程序的主要目的是:使编写的程序代码可以被多种不同类型的对象所重用。
- 并采取一定的语法规则对这个类型进行适当的限定。
- 采用适当的限定语法规则,在编译期进行类型转换的安全检查(没有泛型时,强制类型转换安全检查是在运行时期),既提升安全性,也提升了性能。
泛型类
- 泛型类:具有一个或多个类型变量的类。
- 声明: 声明泛型。(一个类型变量T,用尖括号<>括起来,放在类名后面。可以有多个类型变量,eg:BookStore
泛型方法
- 泛型方法:具有一个或多个类型变量的方法。
- 存在于什么类中:泛型方法既可以定义在普通类中,也可以定义在泛型类中。
- 声明符号的位置: ,位置在修饰符之后,方法返回类型之前。
类型变量的限定
- 类型变量限定:类或方法需要对类型变量加以约束。如限定类型变量T为实现了Comparable接口的类。.
- 一个类型变量或通配符可以有多个限定,用“&”分隔(T extends Father&Comparable&Serializable)。遵循继承规则,可以拥有至多一个类,和多个接口限定。类必须是限定列表中的第一个。
泛型原理(泛型擦除)
- 虚拟机没有泛型类型对象,只有普通类的对象。无论何时定义一个泛型类,都自动提供一个相应的原始类型。原始类型的名字就是删除类型参数后的泛型类型名。
- 原始类型的名字确定:泛型类型变量擦除后,用限定类型列表中的第一个类型进行替换;如果没有限定类型,用Object替换。注意类的名字还原为原始类的名字。
- 泛型的内部机制原理:泛型变量会被擦除替换;为了保证类型安全性,编译器会在必要时插入强制类型转换;当类型擦除后,导致与多态发生冲突时,编译器会生成一个桥方法来保持多态,桥方法里其实就是做一些强制类型转换。
eg:
public class Book<T extends Comparable & Serializable> implements Serializable{ private T first; private T second;}
擦除后:
//类的名字还原为原始类的名字public class Book implements Serializable{//泛型变量擦除后,按规则替换为对应的原始类型名字private Comparable firt;private Comparable second;}
如果调整顺序为:
<T extends Serializable & Comparable>
- 这样做,原始类型用Serializable 替换T。编译器会在必要时,加入Comparable进行强制类型转换。
- 为了提高效率,应将标记接口(标记接口没有方法。常见有:Serializable->支持序列化标记,Cloneable->可深度拷贝标记,RandomAccess->集合元素可通过索引快速访问标记,数组的数据结构有,链表的就没有。)放在限制列表的末尾。
- 虚拟机中,用方法的签名和方法的返回类型二者确定一个方法。
- 编写的程序,根据方法签名和方法入参确定一个方法。
通配符类型
- 通配符类型解决固定的泛型类型(指泛型变量被单一类型实例化)使用的局限性。
- 通配符类型中,允许类型参数变化。
- 通配符“?”同样可以对类型进行限定。可以分为子类型限定;超类型限定;无限定。通配符不是类型变量,因此不能在代码中使用”?”作为一种类型。
<? super Book>//指定下限(超类型限定);安全写,指可安全写入Book,以及Book的子类;因为限定最低泛型类型是Book类型 <? extends Book>//指定上限(子类型限定);安全读,指可把读取的值赋值给Book;因为限定读取到值的最高类型是Book类型,即是Book或Book的子类型 ? obj;//error.'?'不能作为一种类型
- 带有超类型限定的通配符可以向泛型对象写入,即可作为入参类型,为方法提供参数,但不益 作为返回参数类型(返回的类型只能赋给Object)。(如示例中,可以写入Book及其子类型)
- 带有子类型限定的通配符可以从泛型对象中读取,即可以作为返回参数类型。(如示例中,可以读取Book及其子类型)
- 无限定不等于可以传任何值,相反,作为方法的参数时,只能传递null,作为方法的返回时,只能赋给Object。
泛型局限
- 1 不能用基本类型实例化类型参数。因为类型擦除之后,一个类对象如Object,不能存储一个基本类型的值如double类型。
- 2 运行时类型查询只适用于原始类型。
//类型查询只适用于原始类型,故下面的示例都错误 if(b instanceof BookStore<T>)//error if(b instanceof BookStore<MathBook>)//error //warinning->实际转换为了BookStore原始类型 BookStore<MathBook> book = (BookStore<MathBook>)b; /***.getClass()方法也是返回的是原始类型。**/ BookStore<ArithmeticBook> aritBook = new BookStore<>(); BookStore<MathBook> mathBook = new BookStore<>(); if(aritBook.getClass().equals(mathBook.getClass())) { //都是得到原始类型,故一定是相等的。 }
- 3 不能创建参数化类型数组(可以创建参数化类型数组,再进行强制转换);
- 4 可向参数个数可变的方法传递一个泛型类型实例。但会得到一个警告。(可变长度参数实际上就是数组,此时泛型规则对此有所放松。)
- 5 泛型类型变量,不能被实例化;也不能在静态域或方法中引用类型变量;也不能
捕获或抛出泛型变量实例。
obj = new T();//error private static T obj;//error try{...} catch(T e)//error { }
代码示例
泛型类:
/** * @author gao tianci * @version $Id: Pair.java, v 0.1 2017年8月13日 上午11:11:55 gao tianci Exp $ * * 泛型类:具有一个或多个类型变量的类。 * 声明:<T> 声明泛型。(一个类型变量T,用尖括号<>括起来,放在类名后面。可以有多个类型变量,eg:BookStore<T,U>。) * 类型变量:T 代表泛型的类型变量。形式上使用大写字母。用具体类型进行替换,就可以实例化泛型类型。 * 类中定义的泛型类型变量意义:泛型类的类型变量T,可以是该类某个成员方法的返回类型,以及域或是局部变量类型。 */public class BookStore<T> { //T 泛型类型变量作为域变量类型 private T first; private T second; /** * 构造器 */ public BookStore() { super(); first = null; second = null; } /**构造器 * T 泛型类型变量作为成员方法的局部变量类型 * @param first * @param second */ public BookStore(T first, T second) { super(); this.first = first; this.second = second; } //T 泛型类型变量作为成员方法的返回类型 public T getFirst() { return first; } public T getSecond() { return second; } public void setFirst(T first) { this.first = first; } public void setSecond(T second) { this.second = second; }}
通用泛型方法工具类
import java.util.Collection;import org.apache.commons.collections4.CollectionUtils;/** * @author gao tianci * @version $Id: BookStoreUtil.java, v 0.1 2017年8月14日 下午12:42:24 gao tianci Exp $ */public class BookStoreUtil { /** * @param b * @return */ public static boolean hasNulls(BookStore<?> b) { return b.getFirst() == null || b.getSecond() == null; } /** * @param b */ public static void swap(BookStore<?> b) { swapHelper(b); } /** * 泛型方法 * @param b */ public static <T> void swapHelper(BookStore<T> b) { T t = b.getFirst(); b.setFirst(b.getSecond()); b.setSecond(t); } /** * 向参数个数可变的方法,传递一个泛型类型实例。 * 实际args是一个泛型数组。虚拟机也必须对应建立一个泛型数组。泛型对此有所放松。会给出一个警告。可使用@SafeVarargs标注。 * @param collection * @param args * @return */ @SafeVarargs public static <T> Collection<T> addAll(Collection<T> collection, T... args) { if (CollectionUtils.isNotEmpty(collection) && args.length > 0) { //添加 for (T item : args) { collection.add(item); } return collection; } return null; }}
测试类
import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;import java.util.List;/** * @author gao tianci * @version $Id: Test.java, v 0.1 2017年8月15日 下午12:24:46 gao tianci Exp $ */public class Test { @org.junit.Test public void test1() { @SuppressWarnings("unchecked") /** * 创建泛型数组。 * 直接创建不允许。 * BookStore<String>[] books = new BookStore<String>[2];///error * 可声明通配类型数组,然后进行类型转换。从而达到创建泛型数组。 * 建议最好使用:ArrayList<T> 集合来满足泛型数组的需求。 */ BookStore<String>[] books = (BookStore<String>[]) new BookStore<?>[2]; books[0] = new BookStore<>("1", "2"); books[1] = new BookStore<>("3", "4"); //泛型数组转换为对应的List集合 List<BookStore<String>> arrays = Arrays.asList(books); arrays.forEach(item -> System.out.println(item.getFirst() + "," + item.getSecond())); System.out.println("---------"); } @org.junit.Test public void test2() { Collection<BookStore<String>> collection = new ArrayList<>(); collection.add(new BookStore<>("lala", "la")); @SuppressWarnings("unchecked") BookStore<String>[] books = (BookStore<String>[]) new BookStore<?>[2]; books[0] = new BookStore<>("gaga", "ga"); books[1] = new BookStore<>("haha", "ha"); Collection<BookStore<String>> resultCollection = BookStoreUtil.addAll(collection, books[0], books[1]); resultCollection.forEach(item -> System.out.println(item.getFirst() + "," + item.getSecond())); }}
测试结果
1,23,4------lala,lagaga,gahaha,ha
阅读全文
0 0
- Java基础[8]泛型程序设计
- java基础----泛型程序设计
- java基础之泛型程序设计
- JAVA基础【8.1】《Java核心技术1》泛型程序设计-泛型
- Java Applet程序设计基础
- Java Applet程序设计基础
- java基础程序设计
- Java程序设计基础
- Java面试-程序设计基础
- java 基础程序设计
- Java程序设计基础
- Java基础程序设计---数据类型
- Java基础程序设计
- Java程序设计基础
- Java程序设计基础
- JAVA程序设计基础总结
- java基础程序设计
- Java 基础程序设计
- struts2_2
- hibernate5常用jar包
- Leetcode89. Gray Code
- 二叉树的数据结构定义
- leetcode之Word-break
- Java基础[8]泛型程序设计
- swift_异常处理
- 数据结构实验之二叉树一:树的同构
- 渗透测试工具之SQLMap的详细使用方法
- jquery weui实现多tab异步滚动加载更多
- JAVA(0):java环境变量设置
- Java自动装箱、自动拆箱
- Qt Creator 使用笔记
- 在学习前端的路上