Java-泛型
提供编译时类型安全检测机制,该机制允许在编译时检测到非法的类型。泛型本质是参数化类型,即所操作的数据类型被指定为一个参数。可以这样理解参数化类型——具体的类型参数化,只在使用时才指明数据类型。
-
格式
-
<引用数据类型>,例如
<String>
-
<引用数据类型1,引用数据类型2>,例如
<String,Integer>
注意:
< >
里面只能是引用数据类型,比如String、Integer等,而不能是int、double这样的基本数据类型。
-
-
意义
-
将运行时可能产生的错误提前到编译期
-
避免强制类型转换
假如我们现在需要用到Java集合存储元素,并且对集合进行遍历,如下:
import java.util.ArrayList; import java.util.Iterator; public class TestMain { public static void main(String[] args) { ArrayList arrayList=new ArrayList(); arrayList.add("Hello"); arrayList.add("World"); arrayList.add(123); Iterator it = arrayList.iterator(); while (it.hasNext()){ Object o = it.next(); System.out.println(o); } } }
创建一个ArrayList对象arrayList,并且添加了3个元素:“Hello”、“World”、123,现在运行一下看能否正常执行:
Hello
World
123 -
没有问题,正常执行,ArrayList()无参构造方法创建了一个Object类型的数组,而Object类是java所有类的父类,因此任何类的实例都可以被放到这个集合中。
然而一般情况下,我们使用java集合的初衷都是为了存储同一种类型的元素,因为实际情况下需要对集合中的元素进行一系列操作而不仅仅是简单的遍历。假设我创建这个集合对象的初衷是为了存储String类型的数据,而且我想在迭代的时候对元素进行操作,那么我需要把迭代器取出的元素进行强制类型转换,比如:String s=(String)it.next()
,然后运行下面的代码:
import java.util.ArrayList;
import java.util.Iterator;
public class TestMain {
public static void main(String[] args) {
ArrayList arrayList=new ArrayList();
arrayList.add("Hello");
arrayList.add("World");
arrayList.add(123);
Iterator it = arrayList.iterator();
while (it.hasNext()){
String s = (String)it.next();
System.out.println(s);
}
}
}
编译运行结果:
Hello
World
Exception in thread “main” java.lang.ClassCastException
程序抛出了异常,异常类型是ClassCastException,类转换异常。原因是什么?123是一个整型数据,当迭代器迭代到123时,it.next()返回123,String s=(String)123
,这是不被允许的,但是只有在运行时才能发现这个异常,也许我们原本想要add("123")
而不是add(123)
,但是异常确实抛出了。
如果我们使用泛型,会是怎么样?看下面的代码:
import java.util.ArrayList;
import java.util.Iterator;
public class TestMain {
public static void main(String[] args) {
ArrayList<String> arrayList=new ArrayList<String>();
arrayList.add("Hello");
arrayList.add("World");
arrayList.add(123);
Iterator<String> it = arrayList.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
}
}
编译运行:
$ javac TestMain.java
TestMain.java:11: 错误: 不兼容的类型: int无法转换为String
arrayList.add(123);
可以看到,编译时就给我们报错,在IDEA中也会直接检查出来:
这样,就把运行时才能发现的异常提前到编译器,从而对代码进行修改,而且创建迭代器时使用泛型,迭代时也不用进行强制类型转换,比如String s=(String)it.next()
。
-
用途:泛型类、泛型方法、泛型接口。
-
泛型类:
-
格式:
修饰符 class 类名 <T>{ }
,T只是一个标识符,遵守变量名的命名规则。 -
范例
泛型类:
public class Generic <T>{ private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } }
测试类:
package com.day15.generic; public class GenericMain { public static void main(String[] args) { Generic<String> student=new Generic<String>(); student.setT("猪小明"); System.out.println(student.getT()); Generic<Integer> teacher=new Generic<Integer>(); teacher.setT(40); System.out.println(teacher.getT()); } }
编译运行结果:
猪小明
40使用泛型的类,可以在创建实例时才指定数据的类型,可以是任何引用数据类型。
-
-
泛型方法:
-
格式:
修饰符 <类型> 返回值类型 方法名(参数列表){ }
-
范例
GenericMethod类:
public class GenericMethod { //泛型方法 public <T> void show(T t){ System.out.println(t); } }
测试类:
public class GenericMain { public static void main(String[] args) { //----------------泛型方法 GenericMethod gm=new GenericMethod(); gm.show("猪小明"); gm.show(50); gm.show(false); gm.show(12.34); gm.show('c'); gm.show(new Object()); } }
运行结果:
猪小明
50
false
12.34
c
java.lang.Object@6e8dacdf不要疑惑为什么50、false、12.34这样的基本数据类型也能传入,java的自动装箱操作会把它们封装成Integer、Boolean、Double这样的引用数据类型。使用泛型方法相对于泛型类的好处是在创建时不需要指定数据类型,当然我们也可以通过方法重载来实现,不过显然使用泛型方法简便许多。
-
-
泛型接口:
-
格式:
修饰符 interface 接口名 <类型>{ }
-
范例
泛型接口:
public interface GenericInterface<T> { void show(T t); }
实现类:
public class GenericImpl<T> implements GenericInterface<T>{ @Override public void show(T t) { System.out.println(t); } }
测试类:
public class GenericMain { public static void main(String[] args) { GenericInterface<String > gi01=new GenericImpl<>(); gi01.show("12"); GenericInterface<Integer > gi02=new GenericImpl<>(); gi02.show(12); } }
编译运行:
12
12
-
-
仅为学习记录,仅供参考