大连网站建设渠道,公司网站制作要多少钱,东莞网站设计讯息,黄冈网站推广软件ios泛型的理解 泛型的概念 所谓泛型#xff0c;就是允许在定义类、接口时通过一个标识表示类中某个属性的类型 或者是 某个方法的返回值类型及参数类型。这个类型参数将在使用时#xff08;例如#xff0c;继承或实现这个接口#xff0c;用这个类型声明变量、创建对象时#…泛型的理解 泛型的概念 所谓泛型就是允许在定义类、接口时通过一个标识表示类中某个属性的类型 或者是 某个方法的返回值类型及参数类型。这个类型参数将在使用时例如继承或实现这个接口用这个类型声明变量、创建对象时确定即传入实际的类型参数也称为类型实参。
泛型的本质是为了参数化类型在不创建新的类型的情况下通过泛型指定的不同类型来控制形参具体限制的类型。也就是说在泛型使用过程中操作的数据类型被指定为一个参数这种参数类型可以用在类、接口和方法中分别被称为泛型类、泛型接口、泛型方法
泛型的引入背景 集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象所以在JDK1.5之前只能把元素类型设计为ObjectJDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定其他的部分是确定的例如关于这个元素如何保存如何管理等是确定的因此此时把元素的类型设计成一个参数这个类型参数叫做泛型。CollectionListArrayList 这个就是类型参数即泛型。
2.1 类型安全 泛型的主要目标是提高Java程序的类型安全。通过知道使用泛型定义的变量的类型限制编译器可以在非常高的层次上验证类型假设。通过在变量声明中捕获这一附加的类型信息泛型允许编译器实施这些附加的类型约束。类型错误就可以在编译时被捕获了而不是在运行时当作ClassCastException展示出来。将类型检查从运行时挪到编译时有助于Java开发人员更早、更容易地找到错误并可提高程序的可靠性。
//没有泛型的情况
public static void main(String[] args) {ArrayList list new ArrayList();list.add(11);list.add(123);//编译正常
}//有泛型的情况
public static void main(String[] args) {ListString list new ArrayList();list.add(11);list.add(123);//编译报错
}2.2 消除强制类型转换 泛型的一个附带好处是消除源代码中的许多强制类型转换
//没有泛型的代码段需要强制转换
public static void main(String[] args) {List list new ArrayList();list.add(123);Integer integer (Integer) list.get(0);
}//有泛型的代码段不需要强制转换
public static void main(String[] args) {ListInteger list new ArrayListInteger();list.add(1);int s list.get(0);
}2.3 更高的运行效率 ** 避免了不必要的装箱、拆箱操作提高程序的性能。**在非泛型编程中将简单类型作为Object传递时会引起Boxing装箱和Unboxing拆箱操作这两个过程都是具有很大开销的。引入泛型后就不必进行Boxing和Unboxing操作了所以运行效率相对较高特别在对集合操作非常频繁的系统中这个特点带来的性能提升更加明显。
//没有使用泛型
public static void main(String[] args) {//由于是object类型会自动进行装箱操作。Object a 1;//强制转换拆箱操作。这样一去一来当次数多了以后会影响程序的运行效率。int b (int) a;
}//使用了泛型潜在的性能收益
提高了代码的重用性泛型的程序设计意味着编写的代码可以被很多不同类型的对象所重用
泛型的使用 泛型的三种使用方式泛型类泛型方法泛型接口
一般泛型有约定的符号E 代表 Element 通常在集合中使用T 代表 Type通常用于表示类K 代表 KeyV 代表 ValueK, V 通常用于键值对的表示? 代表泛型通配符。
泛型的表达式有如下几种
普通符号 T无边界通配符 ?上界通配符 ? extends E 父类是 E下界通配符 ? super E 是 E 的父类泛型只在编译阶段有效
泛型类 当泛型用在类和接口时就被称为泛型类、泛型接口。这个最典型的运用就是各种集合类和接口比如List、ArrayList 等等。
那么我们泛型怎么用在类上面呢
首先定义一个泛型类。
public class IdGenT {protected T id;public Generic(T id) {this.id id;}
}IdGen 是一个 id 生成类。第一行代码中 是泛型标识代表你定义了一个类型变量 T。第二行代码我使用这个类型变量把 id 定义成一个泛型。
然后在实例化、继承的的时候指定具体的类型。
public class IdGenT {// ..省略部分代码// 通过继承确定泛型变量static class User extends IdGenInteger {public User(Integer id) {super(id);}}public static void main(String[] args) {// 通过实例化确定泛型变量IdGen idGen new IdGenString(1);System.out.println(idGen);User user new User(1);System.out.println(user);}
}用户类继承了 IdGen在代码extends IdGen中指定了 Integer 作为 id 的具体类型而 IdGen 实例化的时候在代码new IdGen(“1”)中则指定了 String 作为 id 的具体类型。
泛型方法 泛型不仅能用在类和接口上还可以用在方法上。
比如怎么把一个类的成员变量转换成 Map 集合呢
这时候我们可以写一个泛型方法。
public class Generic {public static T Map obj2Map(T obj) throws Exception {Map map new HashMap();// 获取所有字段通过 getClass() 方法获取 Class 对象然后获取这个类所有字段Field[] fields obj.getClass().getDeclaredFields();for (Field field : fields) {// 开放字段操作权限field.setAccessible(true);// 设置值map.put(field.getName(), field.get(obj));}return map;}
}同样的 是泛型标识代表你定义了一个类型变量 T用在这个方法上。T obj 使用类型变量 T定义一个 obj 参数。最后在调用方法的的时候再确定具体的类型。
泛型通配符 泛型通配符用 ? 表示代表不确定的类型是泛型的一个重要组成。
有一点很多文章都没提到大家一定要记住
使用泛型有三个步骤定义类型变量、使用类型变量、确定类型变量。在第三步确定类型变量的时候如果你没法明确类型变量这时候可以用泛型通配符。
一般情况下我们不需要用到泛型通配符因为你能明确地知道类型变量你看下面代码。
public class Application {public static Integer count(ListInteger list) {int total 0;for (Integer number : list) {total number;}list.add(total);return total;}public static void main(String[] args) {// 不传指定数据编译报错ListString strList Arrays.asList(0, 1, 2);int totalNum count(strList);// 绕过了编译运行报错List strList1 Arrays.asList(0, 1, 2);totalNum count(strList1);}
}你非常清楚 count() 方法是干什么的所以你在写代码的时候直接就能指明这是一个 Integer 集合。这样一来在调用方法的时候如果不传指定的数据进来就没法通过编译。退一万步讲即使你绕过了编译这一关程序也很可能没法运行。
所以如果你非常清楚自己要干什么可以很明确地知道类型变量那没必要用泛型通配符。
然而在一些通用方法中什么类型的数据都能传进来你没法确认类型变量这时候该怎么办呢
你可以使用泛型通配符这样就不用确认类型变量从而实现一些通用算法。
比如你要写一个通用方法把传入的 List 集合输出到控制台那么就可以这样做。
public class Application {public static void print(List? list) {for (int i 0; i list.size(); i) {System.out.println(list.get(i));}}public static void main(String[] args) {// Integer 集合可以运行ListInteger intList Arrays.asList(0, 1, 2);print(intList);// String 集合可以运行ListString strList Arrays.asList(0, 1, 2);print(strList);}
}List? list 代表我不确定 List 集合装的是什么类型有可能是 Integer有可能是 String还可能是别的东西。但我不管这些你只要传一个 List 集合进来这个方法就能正常运行。
这就是泛型通配符。此外有些算法虽然也是通用的但适用范围不那么大。比如用户分为普通用户、商家用户但用户有一些特殊功能其它角色都没有。这时候又该怎么办呢
你可以给泛型通配符设定边界以此限定类型变量的范围。
泛型通配符的上边界 上边界代表类型变量的范围有限只能传入某种类型或者它的子类。 你看下这幅图就明白了。
利用 ? extends 类名 的方式可以设定泛型通配符的上边界。你看下这个例子就明白了。
public class TopLine {public static void print(List? extends Number list) {for (int i 0; i list.size(); i) {System.out.println(list.get(i));}}public static void main(String[] args) {// Integer 是 Number 的子类可以调用 print 方法print(new ArrayListInteger());// String 不是 Number 的子类没法调用 print 方法print(new ArrayListString());}}你想调用 print() 方法中那么你可以传入 Integer 集合因为 Integer 是 Number 的子类。但 String 不是 Number 的子类所以你没法传入 String 集合。
泛型通配符的下边界 下边界代表类型变量的范围有限只能传入某种类型或者它的父类。你看下这幅图就明白了。 利用 ? super 类名 的方式可以设定泛型通配符的上边界。你看下这个例子就明白了。
public class LowLine {public static void print(List? super Integer list) {for (int i 0; i list.size(); i) {System.out.println(list.get(i));}}public static void main(String[] args) {// Number 是 Integer 的父类可以调用 print 方法print(new ArrayListNumber());// Long 不是 Integer 的父类没法调用 print 方法// print(new ArrayListString());}
}你想调用 print() 方法中那么可以传入 Number 集合因为 Number 是 Integer 的父类。但 Long 不是 Integer 的父类所以你没法传入 Long 集合。
泛型是一种特殊的类型你可以把泛型用在类、接口、方法上从而实现一些通用算法。
此外使用泛型有三个步骤定义类型变量、使用类型变量、确定类型变量。
在确定类型变量这一步中你可以用泛型通配符来限制泛型的范围从而实现一些特殊算法。