Java泛型
2016-03-01 20:27
579 查看
转载自:http://blog.csdn.net/zhai56565/article/details/40503565
[align=center] Java泛型入门
[/align]
1、认识泛型
Java的参数化类型被称为泛型,即允许我们在创建集合时就指定集合元素的类型,该集合只能保存其指定类型的元素。
泛型允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定。例如:
//定义一个接口
interface Money<E>{
Eget(intindex);
boolean add(E
e);
}
//定义一个类
public classApple<T>{
private T info;
public Apple(T
info) {
this.info =
info;
}
public T
getInfo(){
return this.info;
}
public void setInfo(T
info){
this.info =
info;
}
public static void main(String[]
args) {
Apple<String>ap1 = newApple<String>("小苹果");
System.out.println(ap1.getInfo());
Apple<Double>ap2 = newApple<Double>(1.23);
System.out.println(ap2.getInfo());
}
}
需要注意的是,在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用类型形参。因为不管为泛型的类型形参传入哪一种类型实参,对于Java来说,它们依然被当成同一个类处理,在内存中也只占用一块内存空间。不管泛型的实际类型参数是什么,它们在运行时总有同样的类(class),例如下面的程序将输出true。
List<String> l1 = new ArrayList<String>();
List<Double> l2 = new ArrayList<Double>();
System.out.println(l1.getClass()
== l2.getClass());
2、类型通配符
如果我们想定义一个方法,这个方法的参数是一个集合形参,但是集合形参的元素类型是不确定的。可能会想到下面两种定义方法:
public void test1(List l){ }
public void test2(List<Object>
l){ }
test1可以使用,但是会报泛型警告;
test2在传入非List<Object>时无法使用,会报“test2(List<Object>)对于参数(List<String>)不适用”;
为了表示各种泛型List的父类,我们需要使用类型通配符,类型通配符是一个问号(?),将一个问号作为类型实参传给List集合,写作List<?>,那么我们就可以这样定义上面的方法:
public void test3(List<?>
l){ }
此时我们就可以传入以任何元素类型为集合形参的List了,就算是自定义类也可以。
但还有一种特殊的情形,我们不想这个List<?>是任何泛型List的父类,只想表示它是某一类泛型List的父类,例如我们有一个圆形类(Circle)、矩形类(Rectangle),这两个类都继承了Shape抽象类,那么我们就可以这样设计:
public classTest{
public static void main(String[]
args) {
List<Circle>list = newArrayList<Circle>();
list.add(new Circle());
Canvasc = newCanvas();
c.drawAll(list);
}
}
abstract classShape{
public abstract void draw(Canvas
c);
}
class Canvas{
public String
toString() {
return "Canvas";
}
public void drawAll(List<? extends Shape>
shapes){
for (Shape
shape : shapes){
shape.draw(this);
}
}
}
class Circle extends Shape{
public void draw(Canvas
c) {
System.out.println("在画布" +
c + "上画一个圆");
}
}
class Rectangle extends Shape{
public void draw(Canvas
c) {
System.out.println("在画布" +
c + "上画一个矩形");
}
}
List<? extends Shape>是受限制通配符的例子,此处的问号(?)代表一个未知的类型,就想前面看到的通配符一样。但是此处的这个未知类型一定是Shape的子类或Shape本身,因此我们可以把Shape称为这个通配符的上限。
这里需要注意的是,因为不知道这个受限制通配符的具体类型,所以不能把Shape对象或其子类的对象加入这个泛型集合中,例如下面的代码会报编译错误:
public void drawAll(List<? extends Shape>
shapes){
//此处会报编译错误
shapes.add(new Rectangle());
}
Java泛型不仅允许在使用通配符形参时设定上限,而且可以在定义类型形参时设定上限,用于表示传给该类型形参的实际类型要么是该上限类型,要么是该上限类型的子类,例如:
public class Apple<T extends Number>{
T colT;
public static void main(String[]args)
{
Apple<Integer> ai = new Apple<Integer>();
Apple<Double> ad = new Apple<Double>();
//下面代码将引发编译错误,因为String类不是Number的子类型
Apple<String>
as = new Apple<String>();
}
}
3、泛型方法
所谓泛型方法,就是在声明方法时定义一个或多个类型形参。泛型方法的用法格式如下:
修饰符<T,S> 返回值类型
方法名(形参列表){
//方法体
}
把上面方法的格式和普通方法的格式进行对比,不难发现泛型方法的方法签名比普通方法的方法签名多了类型形参声明,类型形参声明以尖括号包括起来,多个类型形参之间以逗号(,)隔开,所有的类型形参声明方法方法修饰符和方法返回值类型之间。
例如,我们要写一个这样的方法,用于将一个Object数组的所有元素添加到一个Collection集合中:
static <T> void fromArrayToCollection(T[]
a,Collection<T> c){
for (T
t : a) {
c.add(t);
}
}
上面的方法是正确的,没有错误,但是在我们实际使用的过程中,我们只能将数组中的元素加入Collection中,想要将一个Collection中的元素加入到另一个Collection中,上面的方法就不适用了。那我们可不可以这样改进上面的方法呢?
static <T> void fromArrayToCollection(Collection<T>
a,Collection<T> b){
for (T
t : a) {
b.add(t);
}
}
public static void main(String[]
args) {
List<String> a = new ArrayList<String>();
List<Object> b = new ArrayList<Object>();
fromArrayToCollection(a,
b);
}
显然,在我们调用fromArrayToCollection时会引发编译错误,这是因为编译器无法准确地推断出泛型方法中类型形参的类型,不知道应该用String还是用Object。为了避免这种错误,可以将方法改为这样:
static <T> void fromArrayToCollection(Collection<? extends T>
a, Collection<T> b){
for (T
t : a) {
b.add(t);
}
}
public static void main(String[]args)
{
List<String> a = new ArrayList<String>();
List<Object> b = new ArrayList<Object>();
fromArrayToCollection(a,
b);
}
上面的方法中,将该方法前一个形参类型改为Collection<? extends T>,这种采用类型通配符的表示方式,只要该方法的前一个Collection集合里的元素类型是后一个Collection集合里的元素类型的子类即可。
4、泛型方法和类型通配符的区别
<T extends A> void test(List<T> list);表示List里面装的某一类型.
void test(List<? extends A> list);表示里面是任一类型.
区别就是某一类型是确定的,通配符只能调用与参数类型无关的方法,而T类型可以调用与参数类型相关的方法。
通配符就是被设计用来支持灵活的子类化的,类型形参T产生的唯一效果是可以在不同的调用点传入不同的实际类型
。
泛型方法允许类型形参被用来表示方法的一个或多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系。如果没有这样的类型依赖关系,就不应该使用泛型方法。
[align=center] Java泛型入门
[/align]
1、认识泛型
Java的参数化类型被称为泛型,即允许我们在创建集合时就指定集合元素的类型,该集合只能保存其指定类型的元素。
泛型允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定。例如:
//定义一个接口
interface Money<E>{
Eget(intindex);
boolean add(E
e);
}
//定义一个类
public classApple<T>{
private T info;
public Apple(T
info) {
this.info =
info;
}
public T
getInfo(){
return this.info;
}
public void setInfo(T
info){
this.info =
info;
}
public static void main(String[]
args) {
Apple<String>ap1 = newApple<String>("小苹果");
System.out.println(ap1.getInfo());
Apple<Double>ap2 = newApple<Double>(1.23);
System.out.println(ap2.getInfo());
}
}
需要注意的是,在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用类型形参。因为不管为泛型的类型形参传入哪一种类型实参,对于Java来说,它们依然被当成同一个类处理,在内存中也只占用一块内存空间。不管泛型的实际类型参数是什么,它们在运行时总有同样的类(class),例如下面的程序将输出true。
List<String> l1 = new ArrayList<String>();
List<Double> l2 = new ArrayList<Double>();
System.out.println(l1.getClass()
== l2.getClass());
2、类型通配符
如果我们想定义一个方法,这个方法的参数是一个集合形参,但是集合形参的元素类型是不确定的。可能会想到下面两种定义方法:
public void test1(List l){ }
public void test2(List<Object>
l){ }
test1可以使用,但是会报泛型警告;
test2在传入非List<Object>时无法使用,会报“test2(List<Object>)对于参数(List<String>)不适用”;
为了表示各种泛型List的父类,我们需要使用类型通配符,类型通配符是一个问号(?),将一个问号作为类型实参传给List集合,写作List<?>,那么我们就可以这样定义上面的方法:
public void test3(List<?>
l){ }
此时我们就可以传入以任何元素类型为集合形参的List了,就算是自定义类也可以。
但还有一种特殊的情形,我们不想这个List<?>是任何泛型List的父类,只想表示它是某一类泛型List的父类,例如我们有一个圆形类(Circle)、矩形类(Rectangle),这两个类都继承了Shape抽象类,那么我们就可以这样设计:
public classTest{
public static void main(String[]
args) {
List<Circle>list = newArrayList<Circle>();
list.add(new Circle());
Canvasc = newCanvas();
c.drawAll(list);
}
}
abstract classShape{
public abstract void draw(Canvas
c);
}
class Canvas{
public String
toString() {
return "Canvas";
}
public void drawAll(List<? extends Shape>
shapes){
for (Shape
shape : shapes){
shape.draw(this);
}
}
}
class Circle extends Shape{
public void draw(Canvas
c) {
System.out.println("在画布" +
c + "上画一个圆");
}
}
class Rectangle extends Shape{
public void draw(Canvas
c) {
System.out.println("在画布" +
c + "上画一个矩形");
}
}
List<? extends Shape>是受限制通配符的例子,此处的问号(?)代表一个未知的类型,就想前面看到的通配符一样。但是此处的这个未知类型一定是Shape的子类或Shape本身,因此我们可以把Shape称为这个通配符的上限。
这里需要注意的是,因为不知道这个受限制通配符的具体类型,所以不能把Shape对象或其子类的对象加入这个泛型集合中,例如下面的代码会报编译错误:
public void drawAll(List<? extends Shape>
shapes){
//此处会报编译错误
shapes.add(new Rectangle());
}
Java泛型不仅允许在使用通配符形参时设定上限,而且可以在定义类型形参时设定上限,用于表示传给该类型形参的实际类型要么是该上限类型,要么是该上限类型的子类,例如:
public class Apple<T extends Number>{
T colT;
public static void main(String[]args)
{
Apple<Integer> ai = new Apple<Integer>();
Apple<Double> ad = new Apple<Double>();
//下面代码将引发编译错误,因为String类不是Number的子类型
Apple<String>
as = new Apple<String>();
}
}
3、泛型方法
所谓泛型方法,就是在声明方法时定义一个或多个类型形参。泛型方法的用法格式如下:
修饰符<T,S> 返回值类型
方法名(形参列表){
//方法体
}
把上面方法的格式和普通方法的格式进行对比,不难发现泛型方法的方法签名比普通方法的方法签名多了类型形参声明,类型形参声明以尖括号包括起来,多个类型形参之间以逗号(,)隔开,所有的类型形参声明方法方法修饰符和方法返回值类型之间。
例如,我们要写一个这样的方法,用于将一个Object数组的所有元素添加到一个Collection集合中:
static <T> void fromArrayToCollection(T[]
a,Collection<T> c){
for (T
t : a) {
c.add(t);
}
}
上面的方法是正确的,没有错误,但是在我们实际使用的过程中,我们只能将数组中的元素加入Collection中,想要将一个Collection中的元素加入到另一个Collection中,上面的方法就不适用了。那我们可不可以这样改进上面的方法呢?
static <T> void fromArrayToCollection(Collection<T>
a,Collection<T> b){
for (T
t : a) {
b.add(t);
}
}
public static void main(String[]
args) {
List<String> a = new ArrayList<String>();
List<Object> b = new ArrayList<Object>();
fromArrayToCollection(a,
b);
}
显然,在我们调用fromArrayToCollection时会引发编译错误,这是因为编译器无法准确地推断出泛型方法中类型形参的类型,不知道应该用String还是用Object。为了避免这种错误,可以将方法改为这样:
static <T> void fromArrayToCollection(Collection<? extends T>
a, Collection<T> b){
for (T
t : a) {
b.add(t);
}
}
public static void main(String[]args)
{
List<String> a = new ArrayList<String>();
List<Object> b = new ArrayList<Object>();
fromArrayToCollection(a,
b);
}
上面的方法中,将该方法前一个形参类型改为Collection<? extends T>,这种采用类型通配符的表示方式,只要该方法的前一个Collection集合里的元素类型是后一个Collection集合里的元素类型的子类即可。
4、泛型方法和类型通配符的区别
<T extends A> void test(List<T> list);表示List里面装的某一类型.
void test(List<? extends A> list);表示里面是任一类型.
区别就是某一类型是确定的,通配符只能调用与参数类型无关的方法,而T类型可以调用与参数类型相关的方法。
通配符就是被设计用来支持灵活的子类化的,类型形参T产生的唯一效果是可以在不同的调用点传入不同的实际类型
。
泛型方法允许类型形参被用来表示方法的一个或多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系。如果没有这样的类型依赖关系,就不应该使用泛型方法。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树