java中的泛型(二)
上一节我介绍了java中泛型的基本原理和使用,今天我介绍java中泛型类型参数的限定和通配符。在java中,泛型是通过类型擦除实现的,泛型是java编译器的概念,java在运行时对与泛型是一无所知的,了解这一点有助于理解java中泛型的一些令人混淆的地方和局限。
在上一篇文章中我们提到了一个词叫做类型参数。关于这个参数我们了解的不多知识把它看成一个Object对象,在java中支持限定这个参数的上界,也就是说参数必须是给定上界的子类或就是给定的上界类型。这个限定通过关键字extends实现。这个上界可以是某个具体的类或者接口。例如:
public class Generic<K extends Number,V extends Number>{ K one; V two; public Generic(K one, V two){ this.one = one; this.two = two; } public K getOne() { return one; } public V getTwo() { return two; } public static void main(String[] args) { Generic<Integer, Double> generic = new Generic<>(2,2.3); Integer one = generic.getOne(); Double two = generic.getTwo(); System.out.println(one); System.out.println(two); } }
另一中比较常见的是限定上界为一个接口,类型参数必须实现这个接口。在泛型方法中有一种场景就是限定的类型必须实现Comparable接口:
public static <T extends Comparable<T>> T max(List<T> arr) { T max = arr.get(0); for (int i = 1; i < arr.size(); i++) { if (arr.get(i).compareTo(max) > 0) { max = arr.get(i); } } return max; }
这个max方法是计算list集合中的最大值,所以需要在list集合中的元素实现Comparable接口。上面介绍的上界都是具体存在的类和接口,在java中允许也上界为一个类型参数。首先我们需要先定义一个容器类:
public class SimpleArrayList<E> { private static final int DEFAULT_CAPACITY = 20; private int size; private Object[] element; public SimpleArrayList() { this.element = new Object[DEFAULT_CAPACITY]; } private void ensureCapacity(int minCapacity) { int oldCapacity = element.length; if (oldCapacity >= minCapacity) { return; } int newCapacity = oldCapacity * 2; if (newCapacity < minCapacity){ newCapacity = minCapacity; } element = Arrays.copyOf(element, newCapacity); } public void add(E e) { ensureCapacity(size + 1); element[size++] = e; } public E get(int index) { return (E) element[index]; } public int size() { return size; } }
这个容器类是使用数组来存放数据的,当存放的数据数量大于数组长度是就会进行扩容。假如现在我们需要在这个容器类中添加一个addAll的方法,我们可以这样来写:
public void addAll(SimpleArrayList<E> c) { for (int i = 0; i < c.size; i++) { add(c.get(i)); } }
这样写看起来并没有什么错误,我们也可以把集合全部添加进去:
public static void main(String[] args) { SimpleArrayList<Number> list = new SimpleArrayList<>(); SimpleArrayList<Number> list1 = new SimpleArrayList<>(); list1.add(1); list1.add(2); list1.add(3); list.addAll(list1); }
可是如果仔细观察会发现list和list1传递进来的参数类型都是Number类型的,如果我们把list1的改成这样:
public static void main(String[] args) { SimpleArrayList<Number> list = new SimpleArrayList<>(); SimpleArrayList<Integer> list1 = new SimpleArrayList<>(); list1.add(1); list1.add(2); list1.add(3); list.addAll(list1); }
那么addAll方法会在编译期间报错。这是因为addAll需要的参数类型是SimpleArray<Number>类型的不是SimpleArray<Integer>类型,即使Integer是Number类型的子类也是不行的。可是我们需求是没有错的,Number的集合当然可以存放Integer,所以我们需要修改一下addAll方法。
public <T extends E> void addAll(SimpleArrayList<T> c) { for (int i = 0; i < c.size; i++) { add(c.get(i)); } }
我们修改addAll方法,其中T是addAll的类型参数,E是SimpleArrayList的类型参数,T的上限是E,这样addAll方法就不会报错了。
在java泛型中还有一个不太好理解的知识点叫做通配符。在上面举得这个例子中如果addAll不使用通配符写难免有一些臃肿,使用通配符后方法就会变得简洁一点。
public void addAll(SimpleArrayList<? extends E> c) { for (int i = 0; i < c.size; i++) { add(c.get(i)); } }
其中?表示通配符<? extends E>叫做有限定通配符,可以匹配E或者E的子类。<T extends E>和<? extends E>有什么区别吗?<T extends E>用来定义类型参数,它声明了一个类型参数T;<? extends E>用来实例化类型参数,它用于实例化泛型变量的类型参数,只是这个实例化的类型是未知的。当使用通配符来实例化类型参数时会有一些限制,例如:
public static void main(String[] args) { List<?> list = new ArrayList<>(); list.add("1"); }
上面的代码会报错,因为使用<?> 和使用<? extends E>来实例化类型参数后集合只能读,不能写。这是因为java无法确保类型的安全性所以只能禁止这样操作。
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Java 比较两个字符串内容是否相等
- 前端模块化发展史
- 前端性能优化
- 突击并发编程JUC系列-原子更新AtomicLong
- Windows下指定的服务已经标记为删除”
- 《剑指offer》第29天:m x n 网格的最小路径和
- 环保 HJ212协议解析
- Flink源码解读系列 | 任务提交流程
- Kafka Topic创建三步曲
- QuickSearch快排
- 二分查找
- LeetCode 1585 Check If String Is Transformable With Substring Sort Operations
- 40000字 Matplotlib 实操干货,真的全!
- 忍术!猫眼三勾玉
- 什么是锁?