[Java5新特性]泛型
来源:互联网 发布:隐形轰炸机 知乎 编辑:程序博客网 时间:2024/04/29 08:25
作者信息
作者姓名:金云龙
个人网站:http://www.longestory.com
个人公众帐号:搜索“longestory”或“龙哥有话说”
Java中集合的问题
Java中的集合有个缺点:就是当我们把数据放置到集合中时,集合是不会记住数据类型的。也就是说,当我们再从集合中获取到数据时,数据类型都变成了Object类型了。
换句话讲,集合对元素类型是没有任何限制的。这样可能会出现一些问题,例如如果我们要创建一个专门存储字符串的List集合的话,也可以将Integer类型数据放置进入。即使放置进去的都是字符串数据,从List集合取出时,还是需要类型转换的(因为集合中元素类型都是Object类型)。
例如下面这个例子:创建一个只保存字符串的List集合,但Integer类型的数字也是可以保存的。在从List集合中取出元素时,是需要强制类型转换的。
public class Demo { public static void main(String[] args) { // 创建一个只保存字符串的List集合 List list = new ArrayList(); list.add("Hello"); list.add("World"); list.add(1000); for (int i = 0; i < list.size(); i++) { String str = (String) list.get(i); System.out.println(str); } }}
上述案例如果想要解决的话,我们可以自定义扩展ArrayList,以保证自定义ArrayList只能保存字符串数据类型。
public class Demo { private List list = new ArrayList(); // 定义自定义ArrayList的add方法 public boolean add(String ele) { return list.add(ele); } // 重写get方法,将get方法的访挥之类型改为String类型. public String get(int index) { return (String) list.get(index); } public int size() { return list.size(); }}
这种方式虽然有效,但局限性很大。在实际开发时,我们需要自定义大量List子类,增大开发工作量,且不具通用性。
什么是泛型
在Java 5版本后,为我们提供了泛型来解决上述问题。首先,我们来查看以下ArrayList的源代码片段:
public class ArrayList<E> { public int size() { return size; } public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } public E get(int index) { rangeCheck(index); return elementData(index); }}
通过查看源代码我们发现,在ArrayList底层代码中,ArrayList类后跟着“”写法,实际上这就是泛型。在ArrayList类的add方法和get方法中,我们也可以看到add方法增加的元素类型是E类型,get方法获取元素返回的类型也是E类型。
所以,上述问题我们可以利用Java 5版本后提供的泛型来解决:
public class Demo { public static void main(String[] args) { // 创建一个只保存字符串的List集合 List<String> list = new ArrayList<String>(); list.add("Hello"); list.add("World"); for (int i = 0; i < list.size(); i++) { String str = (String) list.get(i); System.out.println(str); } }}
那到底什么是泛型呢?泛型其实就是允许在定义类或接口时指定类型形参,这个类型形参将在声明变量、创建对象时确定。说白了,如果集合就像是一个装满东西的瓶子的话,那泛型就像是瓶子上的标签,指定当前这个瓶子只能存放哪一类的东西。
如何定义泛型类
利用泛型定义接口或类的具体方式是怎么样的呢?其实在上述案例中,我们查看的ArrayList类就是一个泛型类,而ArrayList类实现的List就是一个泛型接口。
public class ArrayList<E> { public int size() { return size; } public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } public E get(int index) { rangeCheck(index); return elementData(index); }}
public interface List<E> { int size(); boolean add(E e); E get(int index);}
通过上面两个Java底层提供的案例,我们如果自定义泛型接口或类,应该遵循什么规则呢?
- 在类名后编写“”表示该类接收的是泛型,T就代表泛型。在创建该类对象时,指定具体类型。(T不具备实际含义,这里可以随意指定。)
- 在泛型类中的方法可以接收泛型参数或返回泛型。
- 在泛型类中,泛型只能用于非static成员中。
public class Demo<T> { private T bean; public T getBean() { return bean; } public void setBean(T bean) { this.bean = bean; }}
如何定义泛型方法
除了可以定义泛型接口或类之外,还有一些情况:定义接口或类时没有使用泛型,但定义方法时想自定义类型形参。在Java 5版本后还提供了泛型方法的支持。
所谓泛型方法,就是在声明方式时定义一个或多个类型参数。泛型方法的使用格式如下:
修饰符 <T, S> 返回值类型 方法名(形参列表){ 方法体}
根据这个格式,我们来自定义一个泛型方法:
public class Demo { public static <T> T get(T[] ts) { return ts[0]; } public static void main(String[] args) { Integer[] arr = new Integer[] { 1, 2, 3 }; // 调用方法时,自动识别泛型!因为arr这个参数是Integer[],所以相当于给T赋值为Integer Integer i = get(arr); System.out.println(i); }}
类型通配符
如果我们在使用一个泛型类时,应该为该泛型类传入一个类型实参,如果没有传递类型实参的话,就会引起泛型安全警告。下面我们来看一个例子:
public class Demo { public void demo(List<String> list) { for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } }}
这个案例中,我们定义了一个方法,接收一个泛型为String类型的List集合,并遍历该List集合,打印每一个元素内容。但问题是该方法只能遍历打印泛型为String类型的List集合,如果要想完成同样功能的Integer类型的话,我们需要重新定义一个方法。
public class Demo { public void demo(List<Integer> list) { for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } }}
但问题在于,当上面两个方法出现在同一个类中时,会报错“Method demo(List) has the same erasure demo(List) as another method in type Demo”。原因在于除了泛型不同之外,实际上这两个方法是相同的,而在同一个类中是不允许出现多个相同的方法的。
这个问题的解决方法就是,我们可以将参数中泛型内容去掉,例如下面的代码:
public class Demo { 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) { List<String> strings = null; List<Integer> integers = null; print(strings); print(integers); }}
这样在在print方法中,既可以接收Integer类型的List集合,也可以接收String类型的List集合了。但是,这时会提示泛型安全警告“List is a raw type. References to generic type List should be parameterized”。
要想彻底解决这个问题,我们需要使用类型通配符。通过类型通配符上面的代码可以改写成如下方式:
public class Demo6 { 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) { List<String> strings = null; List<Integer> integers = null; print(strings); print(integers); }}
上述案例中的“
List<? extends Number> list ①List<? super Integer> list ②
这里①表示类型通配符的上边界,含义是当前的泛型可以接收所有Number类型的子类型;②表示类型通配符的下边界,含义是当前的泛型可以接收所有Integer类型的父类型。
转载说明:请注明作者及原文链接,谢谢!
- [Java5新特性]泛型
- Java5.0新特性06-泛型(Generic)
- Java5 新特性
- JAVA5新特性
- 《Java5.0新特性》
- JAVA5.0新特性
- java5的新特性
- java5新特性
- Java5.0新特性
- JAVA5 新特性
- Java5 java6 新特性
- 【转载】JAVA5新特性
- JAVA5新特性
- java5,java6新特性
- Java5.0新特性
- java5新特性
- java5 多线程新特性
- Java5.0新特性
- MySql性能调优(六)InnoDB引擎和Myisam引擎的性能对比
- 一年培训
- 队列
- OAuth 2.0是神马?
- 关于51中断优先级的认识
- [Java5新特性]泛型
- Android数据库高手秘籍(三)——使用LitePal升级表
- 各种排序算法的分析及java实现
- STM32F1驱动AM2302温湿度传感器
- OAuth与SSO、REST有哪些区别与联系
- Android数据库高手秘籍(四)——使用LitePal建立表关联
- leetcode Word Search II
- Alternate linkage specifications
- Android Studio 主题、字体大小的设置