文档章节

Java泛型

韩毅Evan
 韩毅Evan
发布于 2017/07/03 21:31
字数 1805
阅读 11
收藏 0

为什么需要泛型

首先,我们来看一段代码:

import java.util.ArrayList;

public class GenericTest {

	public static void main(String[] args) {

		ArrayList animal = new ArrayList();

		animal.add(new Dog());
		animal.add(new Cat());

		Dog dog = (Dog) animal.get(0);
		Cat cat = (Cat) animal.get(1);

		Cat cat = (Dog) animal.get(0);//报错提示:Type mismatch: cannot convert from Dog to Cat
	}

}

不使用泛型带来的问题:
Dog 和 Cat 都是Animal的子类
ArrayList 默认接受Object类型的对象,所以所有对象都可以放进ArrayList中
所以get(0) 返回的类型是Object
接着,需要进行强制转换才可以得到 Dog 类型或者 Cat 类型。
如果软件开发人员记忆比较好,能记得哪个是哪个,还是可以的。 但是开发人员会犯错误,比如最后一行代码,会记错,把第0个对象转换为 Cat ,这样就会出现类型转换异常 ( java.lang.ClassCastException )

那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现“java.lang.ClassCastException”异常呢?答案就是使用泛型。



什么是泛型?

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

使用泛型的好处:
泛型的用法是在容器后面添加<Type>
Type可以是类,抽象类,接口
泛型表示这种容器,只能存放Dog,Cat就放不进去了。

代码如下:

import java.util.ArrayList;

public class GenericTest {

	public static void main(String[] args) {

		   ArrayList<Dog> animal = new ArrayList<Dog>();
	         
	        //只有Dog可以放进去     
		   animal.add(new Dog());
	         
	        //Cat甚至放不进去
		   animal.add(new Cat());//报错提示:The method add(Dog) in the type ArrayList<Dog> is not applicable for the arguments (Cat)
	         
	        //获取的时候也不需要进行转型,因为取出来一定是Dog
	        Dog dog =  animal.get(0);
	}

}

设置泛型里存放的子类对象

 

假设容器的泛型是Animal,那么 Animal 的子类Dog,Cat都可以放进去
和Animal无关的类型Human还是放不进去

代码如下:

import java.util.ArrayList;

public class GenericTest {

	public static void main(String[] args) {

		ArrayList<Animal> animal = new ArrayList<Animal>();

		// 只有作为animal的子类可以放进去
		animal.add(new Dog());
		animal.add(new Cat());

		// 和Animal无关的类型Human还是放不进去
		animal.add(new Human());
	    // 报错提示:The method add(Animal) in the type ArrayList<Animal> is not applicable for the arguments (Human)


	}

}

泛型的简写

 

为了不使编译器出现警告,需要前后都使用泛型,像这样:

ArrayList<Animal> animal= new ArrayList<Animal>();


不过JDK7提供了一个可以略微减少代码量的泛型简写方式

ArrayList< Animal > animal2 = new ArrayList<>();


后面的泛型可以用<>来代替

 

支持泛型的类

 

设计一个支持泛型的 GenericTest 类
设计这个类的时候,在类的声明上,加上一个<T>,表示该类支持泛型。
T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。

示例代码如下:

public class GenericTest<T> {
	ArrayList<T> objects = new ArrayList<T>(); 
	
	public void add(T t){
		objects.add(t);
	}

	public static void main(String[] args) {
		//在声明这个类的时候,使用泛型<Dog>就表示该类只能放Dog
		GenericTest<Dog> dog = new GenericTest<>();
		
		dog.add(new Dog());//✔
		
		dog.add(new Cat());//报错提示:The method add(Dog) in the type GenericTest<Dog> is not applicable for the arguments (Cat)

        //在声明这个类的时候,使用泛型<Animal>就表示该类只能放Animal,或者其子类
		GenericTest<Animal> animal = new GenericTest<>();
		
		animal.add(new Dog());//✔
        //不能放与Animal无关的Human类
		animal.add(new Human());//报错提示:The method add(Animal) in the type GenericTest<Animal> is not applicable for the arguments (Human)

	}

}

? extends

ArrayList animals<? extends Animal> 表示这是一个Animal 泛型或者其子类泛型
animals 的泛型可能是 Animal
animals 的泛型可能是 Dog
animals 的泛型可能是 Cat
所以 可以确凿的是,从 animals 取出来的对象,一定是可以转型成 Animal 的

但是,不能往里面放东西,因为
放 Dog  不满足 < Cat >
放 Cat   也不满足< Dog >

示例代码如下:

import java.util.ArrayList;

public class GenericTest<T> {

	public static void main(String[] args) {
		ArrayList<Cat> cats = new ArrayList<Cat>();
		cats.add(new Cat());

		ArrayList<? extends Animal> animals = cats;

		// ? extends Animal 表示这是一个Animal泛型的子类泛型

		// animals 的泛型可以使Animal
		// animals 的泛型可以使Cat
		// animals 的泛型可以使Dog

		// 可以确凿的是,从animals取出来的对象,一定是可以转型成Animal的

		Animal animal = animals.get(0);

		// 但是,不能往里面放东西
		animals.add(new Dog()); // 编译错误,因为 animals 的泛型 有可能是Cat
		//错误提示:The method add(capture#2-of ? extends Animal) in the type ArrayList<capture#2-of ? extends Animal> is not applicable for the arguments (Dog)
	}

}

? super

ArrayList animals<? super Animal> 表示这是一个Animal泛型或者其父类泛型
Animal 的泛型可能是 Animal
Animal 的泛型可能是 Object

可以往里面插入 Animal 以及 Animal 的子类
但是取出来有风险,因为不确定取出来是 Animal 还是Object

示例代码如下:

import java.util.ArrayList;

public class GenericTest<T> {

	public static void main(String[] args) {
		ArrayList<? super Animal> animals = new ArrayList<Object>();
        
        //? super Animal 表示 animals的泛型是Animal或者其父类泛型
          
        //animals 的泛型可以是Animal
        //animals 的泛型可以是Object
          
        //所以就可以插入Animal
		animals.add(new Animal());
        //也可以插入Animal的子类
		animals.add(new Cat());
		animals.add(new Dog());
          
        //但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Animal会失败
        Animal animal= animals.get(0);//报错提示:	Type mismatch: cannot convert from capture#4-of ? super Animal to Animal
          
    }
}

泛型通配符 ?

泛型通配符? 代表任意泛型
既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能

所以只能以Object的形式取出来
并且不能往里面放对象,因为不知道到底是一个什么泛型的容器

import java.util.ArrayList;

public class GenericTest<T> {

	public static void main(String[] args) {
		ArrayList<Cat> cats = new ArrayList<Cat>();

		// ?泛型通配符,表示任意泛型
		ArrayList<?> generalList = cats;

		// ?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型
		// 所以只能以Object的形式取出来
		Object o = generalList.get(0);

		// ?的缺陷2: 既然?代表任意泛型,那么既有可能是Animal,也有可能是Human
		// 所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去
		generalList.add(new Human()); // 编译错误 因为?代表任意泛型,很有可能不是Human
		generalList.add(new Animal()); // 编译错误 因为?代表任意泛型,很有可能不是Animal
		generalList.add(new Cat()); // 编译错误 因为?代表任意泛型,很有可能不是Cat

	}
}

总结

如果希望只取出,不插入,就使用? extends
如果希望只插入,不取出,就使用? super
如果希望,又能插入,又能取出,就不要用通配符?

 

PS:
子类泛型不可以转换为父类泛型

父类泛型不可以转型为子类泛型

© 著作权归作者所有

韩毅Evan
粉丝 2
博文 49
码字总数 67347
作品 0
南京
私信 提问

暂无文章

关于AsyncTask的onPostExcute方法是否会在Activity重建过程中调用的问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/XG1057415595/article/details/86774575 假设下面一种情况...

shzwork
今天
7
0
object 类中有哪些方法?

getClass(): 获取运行时类的对象 equals():判断其他对象是否与此对象相等 hashcode():返回该对象的哈希码值 toString():返回该对象的字符串表示 clone(): 创建并返此对象的一个副本 wait...

happywe
今天
6
0
Docker容器实战(七) - 容器中进程视野下的文件系统

前两文中,讲了Linux容器最基础的两种技术 Namespace 作用是“隔离”,它让应用进程只能看到该Namespace内的“世界” Cgroups 作用是“限制”,它给这个“世界”围上了一圈看不见的墙 这么一...

JavaEdge
今天
8
0
文件访问和共享的方法介绍

在上一篇文章中,你了解到文件有三个不同的权限集。拥有该文件的用户有一个集合,拥有该文件的组的成员有一个集合,然后最终一个集合适用于其他所有人。在长列表(ls -l)中这些权限使用符号...

老孟的Linux私房菜
今天
7
0
面试套路题目

作者:抱紧超越小姐姐 链接:https://www.nowcoder.com/discuss/309292?type=3 来源:牛客网 面试时候的潜台词 抱紧超越小姐姐 编辑于 2019-10-15 16:14:56APP内打开赞 3 | 收藏 4 | 回复24 ...

MtrS
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部