Java泛型
来源:互联网 发布:利用js实现动态时间 编辑:程序博客网 时间:2024/06/03 17:01
1、泛型使用:
在Java 7 以前,如果使用带泛型的接口、类定义变量,那么调用构造器创建对象时构造器的后面也必须带泛型,这显得有些多余了。例如如下两条语句:
List<String> strList = new ArrayList<String>(); Map<String, Integer> scores = new HashMap<String, Integer>();
从Java 7开始,Java允许在构造器后不需要带完整的泛型信息,只要给出一对尖括号(<>)即可,Java可以推断尖括号里应该是什么泛型信息。即上面两条语句可以改写为如下形式:
List<String> strList = new ArrayList<>(); Map<String, Integer> scores = new HashMap<>();
2、并不存在泛型类
ArrayList<String> 类像是一种特殊的ArrayList 类:该ArrayList<String> 对象只能添加String 对象作为集合元素。但实际上,系统并没有为ArrayList<String> 生成新的class文件,而且也不会把ArrayList<String> 当成新类来处理。
看下面代码的打印结果是什么?
// 分别创建List<String> 对象和List<Interger>对象 List<String> l1 = new ArrayList<>(); List<Integer> l2 = new ArrayList<>(); // 调用getClass() 方法来比较l1和l2的类是否相等 System.out.println(l1.getClass() == l2.getClass());
运行上面的代码片段,实际输出的是true。因为不管泛型的实际类型参数是什么,它们在运行时总是同样的类(class).
不管为泛型的类型形参传入哪一种类型参数,对于Java来说,它们依然被当成同一个类处理在内存中也只占用一块内存空间,因此在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用类型形参。
3、类型通配符
提出一个问题:如果Foo是Bar 的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G<Foo>是否是G<Bar>的子类型?答案是否定的。数组和泛型有所不同,假设Foo是Bar的一个子类型(子类或者子接口),那么Foo[]依然是Bar[]的子类型;但G<Foo>不是G<Bar>的子类型。
(1) 使用类型通配符
为了表示各种泛型List 的父类,可以使用类型通配符,类型通配符是一个问号(?),将一个问号作为类型实参传给List 集合,写作:List<?>(意思是元素类型位置的List)。这个问号(?) 被称为通配符,它的元素类型可以匹配任何类型。
但这种带通配符的List 仅表示它是各种泛型List 的父类,并不能把元素加入到其中。例如,如下代码将会引起编译错误。
List<?> c = new ArrayList<String>();
// 下面程序引起编译错误
c.add(new Object());
因为程序无法确定c集合中元素的类型,所以不能向其中添加对象。根据前面的List<E> 接口定义的代码可以发现:add()方法有类型参数E 作为集合的元素类型,所以传给add 的参数必须是E 类的对象或其子类的对象。但因为在该例中不知道E 是什么类型,所以程序无法将任何对象“丢进”该集合。唯一的例外是null,它是所有引用类型的实例。
(2) 设定类型通配符的上限
当直接使用List<?> 这种形式时,即表明这个List 集合可以是任何泛型List 的父类。但还有一个特殊的情形,程序不希望这个List<?> 是任何泛型List 的父类,只希望它代表某一类泛型List 的父类。考虑一个简单的绘图程序,下面先定义三个形状类。
// 定义一个抽象类Shape public abstract class Shape { public abstract void draw(Canvas c); }
// 定义Shape 的子类Circle public class Circle extends Shape { // 实现画图方法,以打印字符串来模拟画图方法实现 public void draw(Canvas c) { System.out.println("在画布" + c + "上画一个圆"); } }
// 定义Shape的子类Rectangle public class Rectangle extends Shape { // 实现画图方法,以打印字符串来模拟画图方法实现 public void draw(Canvas c) { System.out.println("把一个矩形画在画布" + c + "上"); } }
上面定义了三个形状类,其中Shape 是一个抽象父类,该抽象父类有两个子类:Circle 和 Rectangle 。接下来定义一个Canvas类,该画布类可以画数量不等的形状(Shape 子类的对象),那应该如何定义这个Canvas类呢?考虑如下的Canvas实现类。
public class Canvas { // 同时在画布上绘制多个形状 public void drawAll(List<Shape> shapes) { for(Shape s : shapes) { s.draw(this); } } }
注意上面的drawAll()方法的形参类型是List<Shape>,而List<Circle>并不是List<Shape>的子类型,因此,下面代码将引起编译错误。
List<Circle> circleList = new ArrayList<>(); Canvas c = new Canvas();
不能把List<Circle>当成List<Shape>使用,所以不能把List<Circle>对象当成List<Shape>使用。为了表示List<Circle>的父类,可以使用List<?>,把Canvas 改成如下形式:
public class Canvas { // 同时在画布上绘制多个形状 public void drawAll(List<?> shapes) { for(Object obj : shapes) { Shape s = (Shape) obj; s.draw(this); } } }
上面程序使用了通配符来表示所有的类型。上面的drawAll()方法可以接受List<Circle> 对象作为参数,问题是上面的方法实现体显得极为臃肿而烦琐:使用了泛型还需要进行强制类型转换。
实际上需要一种泛型表示方法,它可以表示所有的Shape泛型List 的父类。为了满足这种需求,Java泛型提供了被限制的泛型通配符。被限制的泛型通配符表示如下:
// 它表示所有Shape泛型List 的父类 List<? extends Shape> 有了这种被限制的泛型通配符,就可以把上面的Canvas程序改为如下形式: public class Canvas { // 同时在画布上绘制多个形状,使用被限制的泛型通配符 public void drawAll(List<? extends Shape> shapes) { for(Shape s : shapes) { s.draw(this); } } }
将Canvas改为如上形式,就可以把List<Circle> 对象当成List<? extends Shape> 使用。即List<? extends Shape> 可以表示List<Circle>、List<Rectangle>的父类--只要List后尖括号里的类型是Shape的子类型。
类似地,由于程序无法确定这个受限制的通配符的具体类型,所以不能把Shape对象或其子类的对象加入到这个泛型集合中。例如,下面代码就是错误的。
public void addRectangle(List<? extends Shape> shapes) { // 下面代码引起编译错误 shapes.add(new Rectangle()); }
(3) 设定类型形参的上限
Java 泛型不仅允许在使用通配符形参时设定上限,而且可以在定义类型形参时设定上限,用于表示传给该类型形参的实际类型要么是该上限类型,要么是该上限类型的子类。下面程序示范了这种用法。
public class Apple<T extends Number> { T col; public static void main(String[] args) { Apple<Integer> ai = new Apple<>(); Apple<Double> ad = new Apple<>(); // 下面代码将引发编译异常,下面代码试图把String类型传给T形参 // 但String 不是Number 的子类型,所以引起编译错误 Apple<String> as = new Apple<>(); } }
1 0
- 【java 2】java泛型
- Java 泛型 Java generic
- Java Tutorials_Generics(java泛型)
- Java基础 Java 泛型
- java 泛型
- java泛型
- Java泛型
- Java泛型
- java泛型
- java泛型
- java泛型
- Java 泛型
- Java泛型
- Java 泛型
- JAVA 泛型
- java 泛型
- java泛型
- Java泛型
- Linux下修改MySQL的root用户密码
- Mysql简单入门
- python核心编程-线程threading模块二
- 电商活动中刮刮卡的实现
- DeepLearning tutorial
- Java泛型
- 从零开始搭建Hadoop2.7.1的分布式集群
- Unity 编辑器环境下不能正确加载Android Assetbundle 中的 Shader 解决方法
- [Leetcode]68. Text Justification @python
- 整数分解成素数【待解决】
- linux下简单的测试cpu性能的方法
- poj 3278 Catch That Cow(bfs)
- 关于该死的HibernateDaoSupport
- 算法与数据结构学习资源大搜罗——良心推荐