文章目录
- 1.泛型概念的提出
- 2.泛型集合
- 3.泛型方法
- 4.泛型类
- 5.泛型接口
- 6.泛型通配符 ?
- 7.总结
1.泛型概念的提出
泛型,即“参数化类型”。Java5
开始出现的一种对Java语 言类型的一种拓展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使 用参数类型时指定的类型占位符,就好比方法的形式参数是实际参数的占位符一样.
- Tiger类
public class Tiger {
String name;
public Tiger() {
}
public Tiger(String name) {
this.name = name;
}
@Override
public String toString() {
return "Tiger@name:" + this.name;
}
public void eat() {
System.out.println(this.name + "吃羊");
}
}
- Sheep类
class Sheep {
String name;
public Sheep() {
}
public Sheep(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep@name:" + this.name;
}
public void eat() {
System.out.println(this.name + "吃青草");
}
}
- 测试类
public class Test {
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add(new Tiger("华南虎"));
arr.add(new Tiger("东北虎"));
arr.add(new Sheep("喜羊羊"));
System.out.println(arr);
Iterator it = arr.iterator();
while (it.hasNext()) {
Object next = it.next();
Tiger t = (Tiger) next;
t.eat();
}
}
}
原因 :发现虽然集合可以存储任意对象,但是如果需要使用对象的特有方法,那么就需要类型转换,如果集合中存入的对象不同,可能引发类型转换异常.
- 出现问题
存入的是特定的对象,取出的时候是Object
对象,需要强制类型转换,可能诱发类型转换异常.无法控制存入的是什么类型的对象,取出对象的时候进行强转时可能诱发异常
.而且在编译时期无法发现问题.
虽然可以再类型转换的时候通过if语句进行类型检查(instanceof
),但是效率较低.(例如吃饭的时候,还需要判断米饭里有没有沙子,吃饭效率低).可以通过给容器加限定的形式规定容器只能存储一种类型的对象.
就像给容器贴标签说明该容器中只能存储什么样类型的对象。
所以在jdk5.0后出现了泛型!
泛型的基本术语,以ArrayList<E>
为例:<>
念着typeof
ArrayList<E>
中的E称为类型参数变量
ArrayList<Integer>
中的Integer
称为实际类型参数。整个称为ArrayList<E>
泛型类型。整个ArrayList<Integer>
称为参数化的类型ParameterizedType
2.泛型集合
- 格式
集合类<类类型> 变量名 = new 集合类<类类型>();
public static void main(String[] args) {
// 使用泛型后,规定该集合只能放羊,老虎就进不来了.
ArrayList<Sheep> arr = new ArrayList<Sheep>();
arr.add(new Sheep("美羊羊"));
arr.add(new Sheep("懒洋洋"));
arr.add(new Sheep("喜羊羊"));
// 编译失败
// arr.add(new Tiger("东北虎"));
System.out.println(arr);
Iterator<Sheep> it = arr.iterator();
while (it.hasNext()) {
// 使用泛型后,不需要强制类型转换了
Sheep next = it.next();
next.eat();
}
}
- 将运行时的异常提前至编译时发生。
- 获取元素的时候无需强转类型,就避免了类型转换的异常问题
- 格式 通过
<>
来指定容器中元素的类型. - 什么时候使用泛型: 当类中操作的引用数据类型不确定的时候,就可以使用泛型类.
- JDK5.0之前的
Comparable
package java.lang;
public interface Comparable {
public int compareTo(Object o);
}
- JDK5.0之后的
Comparable
package java.lang;
public interface Comparable<T> {
public int compareTo(T o);
}
这里的<T>
表示泛型类型,随后可以传入具体的类型来替换它.
- 细节一: 声明好泛型类型之后,集合中只能存放特定类型元素
public static void main(String[] args) {
// 创建一个存储字符串的list
ArrayList<String> arr = new ArrayList<String>();
arr.add("tom");
arr.add("jack");
// 存储非字符串编译报错.
arr.add(1);
}
- 细节二: 泛型类型必须是引用类型
public static void main(String[] args) {
// 泛型类型必须是引用类型,也就是说集合不能存储基本数据类型
// ArrayList<int> arr2=new ArrayList<int>();
// 使用基本数据类型的包装类
ArrayList<Integer> arr2 = new ArrayList<Integer>();
}
- 细节三: 使用泛型后取出元素不需要类型转换.
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<String>();
arr.add("北京");
arr.add("上海");
arr.add("广州");
// 使用泛型后取出元素不需要类型转换.
String str = arr.get(0);
System.out.println();
}
3.泛型方法
- 函数上的泛型定义。当函数中使用了一个不明确的数据类型,那么在函数上就可以进行泛型的定义。
public <泛型的声明> 返回值类型 函数名( 泛型 变量名 ){
}
public class GenericMethodTest {
// 泛型方法 printArray
public static <E> void printArray(E[] inputArray) {
// 输出数组元素
for (E element : inputArray) {
System.out.println(element);
}
System.out.println();
}
public static void main(String args[]) {
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println("整型数组元素为:");
printArray(intArray); // 传递一个整型数组
System.out.println("\n双精度型数组元素为:");
printArray(doubleArray); // 传递一个双精度型数组
System.out.println("\n字符型数组元素为:");
printArray(charArray); // 传递一个字符型数组
}
}
- 有界的类型参数
可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。
要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界
- 实例
该例子中的泛型方法返回三个可比较对象的最大值。
public class MaximumTest {
// 比较三个值并返回最大值
public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
T max = x; // 假设x是初始最大值
if (y.compareTo(max) > 0) {
max = y; // y 更大
}
if (z.compareTo(max) > 0) {
max = z; // 现在 z 更大
}
return max; // 返回最大对象
}
public static void main(String args[]) {
System.out.println("最大的数为是:"+maximum(3, 4, 5));
System.out.println("最大的数为是:"+maximum(6.6, 8.8, 7.7));
System.out.println("最大的数为是:"+maximum("pear", "apple", "orange"));
}
}
4.泛型类
- 格式
类上的泛型声明
修饰符 class 类名<泛型>{
}
- 实例
public class Demo1 {
public static void main(String[] args) {
Father<String> f = new Father<String>("jack");
System.out.println(f.getT());
Father<Integer> f2 = new Father<Integer>(20);
System.out.println(f2.getT());
}
}
class Father<T> {
private T t;
public Father() {
}
public Father(T t) {
super();
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
如果Father
类有子类,子类该如何实现
public class Demo1 {
public static void main(String[] args) {
Father<String> f = new Father<String>("jack");
System.out.println(f.getT());
Father<Integer> f2 = new Father<Integer>(20);
System.out.println(f2.getT());
}
}
class Father<T> {
private T t;
public Father() {
}
public Father(T t) {
super();
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
// 子类指定了具体的类型
class Son extends Father<String> {
}
// 子类也需要使用泛型
class Son3<T> extends Father<T> {
}
// 错误写法,父类上定义有泛型需要进行处理
class Son2 extends Father<T> {
}
注意:静态方法不可以使用类中定义的泛型
因为类中的泛型需要在对象初始化时指定具体的类型,而静态优先于对象存在。那么类中的静态方法就需要单独进行泛型声明
,声明泛型一定要写在static后,返回值类型之前
public class Demo6<T> {
public static void main(String[] args) {
System.out.println(new Demo6<Integer>().getData(100));
}
public T getData(T data) {
return data;
}
// 静态方法
public static T getData2(T data) {
return data;
}
//泛型类中 静态方法 类方法 类属性
public static <T> T getData2(T data) {
return data;
}
}
5.泛型接口
public class Demo8 {
public static void main(String[] args) {
MyInter<Integer> my = new MyInter<Integer>();
my.print(200);
MyInter2 my2 = new MyInter2();
my2.print("只能传字符串");
}
}
interface Inter<T> {
void print(T t);
}
// 实现不知为何类型时可以这样定义
class MyInter<T> implements Inter<T> {
public void print(T t) {
System.out.println("myprint:" + t);
}
}
// 使用接口时明确具体类型。
class MyInter2 implements Inter<String> {
@Override
public void print(String t) {
System.out.println("myprint:" + t);
}
}
6.泛型通配符 ?
public class Demo9 {
public static void main(String[] args) {
ArrayList<Object> arr = new ArrayList<Object>();
arr.add(new Tiger("阿虎"));
arr.add("你好");
print(arr);
// 将集合的泛型设置类String类型,是Object子类
HashSet<String> hs = new HashSet<String>();
hs.add("hello");
hs.add("jack");
// 由于print方法接收的集合进行了元素限定,只接受限定为Object类型的集合,编译不通过
print(hs);
}
public static void print(Collection<Object> coll) {
Iterator<Object> it = coll.iterator();
while (it.hasNext()) {
Object next = it.next();
System.out.println(next);
}
}
}
但是,由于print
方法接收的集合进行了元素限定,只接受限定为Object
类型的集合,编译不通过该问题如何解决?
可以把方法的形参的泛型去掉,那么方法中就把集合中的元素当做Object
类型处理.
也可以使用使用泛型通配符
public static void main(String[] args) {
ArrayList<Object> arr = new ArrayList<Object>();
arr.add(new Tiger("阿虎"));
arr.add("你好");
print(arr);
// 将集合的泛型设置类String类型,是Object子类
HashSet<String> hs = new HashSet<String>();
hs.add("hello");
hs.add("jack");
// 由于print方法接收的集合进行了元素限定,只接受限定为Object类型的集合,编译不通过
print(hs);
}
public static void print(Collection<?> coll) {
Iterator<?> it = coll.iterator();
while (it.hasNext()) {
Object next = it.next();
System.out.println(next);
}
}
上述就使用了泛型通配符
通配符:?
public void show(List<?> list)
{
}
可以对类型进行限定范围。
?extends E: 接收E类型或者E的子类型。
? super E: 接收E类型或者E的父类型。
限定泛型通配符的边界
- 限定通配符的上边界:
extends
public class Demo10 {
public static void main(String[] args) {
firstPrint();
secondPrint();
}
public static void print(List<? extends Number> list) {// 通配符作形参
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));// 使用get方法
}
}
public static void firstPrint() {
List<Float> list1 = new ArrayList<>();//使用 Float作实参类型
list1.add(12.3f);
list1.add(23.4f);
print(list1);
}
public static void secondPrint() {
List<Integer> list2 = new ArrayList<>();//使用Integer作实参类型
list2.add(1);
list2.add(2);
list2.add(3);
print(list2);
}
public static void thirdPrint() {
List<String> list = new ArrayList<>();//使用Integer作实参类型
list.add("aa");
list.add("bb");
list.add("cc");
print(list);
}
}
- 限定通配符的下边界
super
public class Demo11 {
public static void firstInflate(List<? super Integer> list) {// 装填整数
list.add(1);
list.add(2);
list.add(3);
}
public static void secondInflate(List<? super Float> list) {// 装填浮点数
list.add(1.1f);
list.add(2.2f);
list.add(1.3f);
}
public static void main(String[] args) {
List<In> list = new ArrayList<>();
firstInflate(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("------------------------------");
list.clear();
secondInflate(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
7.总结
JDK5
中的泛型允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定
如果有泛型,那么在编译的时候就可以把异常显示出来!
注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛型的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。