概念:
百度百科:泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。
注意:在java中只有引用数据类型才能作为参数类型传递,基本数据类型不能。
优点:
1.保证数据类型的安全性。
public class genericity {
public static void main(String[] args) {
//这里创建了一个泛型为Integer类型的ArrayList
ArrayList<Integer> list = new ArrayList<Integer>();
//向这个list中添加数据
list.add(1);
//当添加String类型的数据的时候就报参数不适用的错误
//因为创建的时候泛型传入Integer类型就限定了该list只能添加Integer类型的数据
list.add("1");
}
}
2.可以避免类型的强制转换。
//这里创建了一个没有传入泛型的ArrayList
ArrayList list2 = new ArrayList();
//这个list2既能添加Integer类型的数据也能添加String类型的数据
//因为不传入泛型类型的话默认是Object类型
list2.add(1);
list2.add("1");
//这里取出来的值都是Object类型
Object i1 = list2.get(0);
Object s1 = list2.get(1);
//如果要得到传入时的类型就要进行显示的手动的强制类型转换
Integer i2 = (Integer)list2.get(0);
String s2 = (String)list2.get(1);
但使用泛型的时候其实也进行了强制类型的转换,只不过这些强制类型转换在编译成字节码文件的时候还是会自动的隐式的进行。
我们反编译字节码文件就可以看到隐式的强制类型转换
源代码:
public class genericity
{
public static void main(String[] args)
{
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
Integer i = list.get(0);
}
}
反编译:
public class genericity
{
public static void main(String[] args)
{
ArrayList<Integer> list = new ArrayList();
list.add(Integer.valueOf(1));
Integer i = (Integer)list.get(0);
}
}
3.将运行时期的类型转换异常问题,提前到编译时期。
//如果要得到传入时的类型就要进行显示的手动的强制类型转换
Integer i2 = (Integer)list2.get(0);
String s2 = (String)list2.get(1);
//但如果强转的类型出错,在编译时期不会报错但运行的时候就会报"...cannot be cast to..."的异常
//如果使用泛型则在添加数据的时候就规定死了只能添加某种数据类型的数据
//如果添加的数据不符合编译的时候就会报错,就把运行的时候隐式的强转带来的错误提前到了编译时期
Double i3 = (Double)list2.get(0);
Double s3 = (Double)list2.get(1);
4.提高代码的重用率
这里是一个用泛型写的比较两个数据是否相等的比较类
public class compare<T> {
private T a;
private T b;
public compare(T a, T b) {
this.a = a;
this.b = b;
}
public boolean Compare() {
return a.equals(b) ? true : false;
}
}
主函数
public class compare_main {
public static void main(String[] args) {
compare<Integer> com1 = new compare<Integer>(1, 1);
System.out.println(com1.Compare());
compare<String> com2 = new compare<String>("a", "b");
System.out.println(com2.Compare());
}
}
结果
true
false
如果不使用泛型,可以用Object来写,不过这样就要手动进行强制类型转换,或者每个类型都写一个类来进行比较,这样就会产生多个代码逻辑一样只是类型不一样的比较类,用泛型的话就提高了代码的重用率只要写一个就可以了。
泛型的限制:
如果某个操作要求传入的类型必须是某个类型的子类,以便于重写或使用该子类的方法,则可以对传入的泛型有所限制,避免传入的类型没有某个方法或属性导致异常。这也增强了数据类型的安全性。
<T extends SomeClass>表示该泛型所能传入的类型是SomeClass类型的子类。
当然也同样适用于extends接口来限制泛型传入的类型,并且可以多接口限定,如果限定中包含类和接口则需要把类写在最前面,并且多限定的时候只能extends一个类。
<T extends SomeClass & interface1 & interface2 & interface3>
public class limit <T extends List<?>>{
private T list;
public limit(T list) {
this.list = list;
}
public void show() {
//这里用List的迭代器遍历输出list
Iterator<?> i = list.iterator();
while(i.hasNext()) {
System.out.println(i.next());
}
}
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
limit<ArrayList<String>> lim = new limit<ArrayList<String>>(list);
lim.show();
HashMap<String, String> map = new HashMap<String, String>();
map.put("1", "a");
map.put("2", "b");
map.put("3", "c");
//这里创建limi的时候就会报错,因为HashMap并没有实现List<E>接口
//limit<ArrayList<String>> limi = new limit<ArrayList<String>>(map);
//limi.show();
}
}
泛型方法:
public class function <T>{
private T a;
//这里泛型类型的变量a不能声明为static
//private static T a;
public function(T a) {
this.a = a;
}
public void Show() {
//这里输出a类型的name
System.out.println("类的泛型类型参数");
System.out.println(a.getClass().getName());
}
//泛型方法
//这里返回值类型也可以用泛型表示
//注意这里的T并不是类中传递过来的泛型参数T,这是一个只属于这个方法的泛型,你也可以写成别的字母
public <T> T GenFunction(T t) {
//这里输出t类型的name
System.out.println("泛型方法");
System.out.println(t.getClass().getName());
//泛型方法中如果对t的强制类型转换出错的话也会报异常
//使用泛型方法的时候不需要声明传入的泛型类型,这里的数据其实不怎么安全
//一般定义泛型方法的时候会对泛型类型进行限制,或者使用通配符
//String in = (String) t;
return t;
}
// 这里在静态方法中不能引用在类中传递过来的泛型T
// public static void StaticFunction(T t) {
//
// }
//如果静态方法要用泛型只能声明该方法为泛型方法
public static <S> void StaticFunction(S s) {
System.out.println("静态泛型方法");
System.out.println(s.getClass().getName());
}
public static void main(String[] args) {
function<String> f = new function<String>("zzf");
f.Show();
f.GenFunction(123);
f.StaticFunction(10.0);
}
}
结果
类的泛型类型参数
java.lang.String
泛型方法
java.lang.Integer
静态泛型方法
java.lang.Double
注意:如果类中有静态方法则该静态方法不能引用类中声明的泛型,如果静态方法要使用泛型则要单独写成泛型方法。那么静态方法能不能引用泛型类型的成全变量呢?答案也是不能,因为静态方法中引用的变量必须也是静态的,而泛型类型的变量不能用static关键字声明。
泛型通配符:
在定义泛型属性或者泛型方法的时候不想定义的时候就把泛型的类型写死,而是想根据后面的代码逻辑动态的进行泛型类型的参数传递,增加属性和方法的通用性,就可以使用通配符,通配符还可以限定传入泛型的类型范围
class Genwildcard <T>{
private T a;
public Genwildcard(T a) {
this.a = a;
}
public void show() {
System.out.println(a.getClass().getName());
}
}
public class wildcard {
//使用通配符定义方法
public static void FunWildCard1(Genwildcard<?> g) {
g.show();
}
//定义泛型方法达到同样的效果
public static <T> void FunWildCard2(Genwildcard<T> g) {
g.show();
}
//不规范写法达到同样效果
public static void FunWildCard3(Genwildcard g) {
g.show();
}
public static void main(String[] args) {
//使用通配符定义属性
Genwildcard<?> gwc;
//不过实例化的时候必须声明泛型的类型
gwc = new Genwildcard<Integer>(123);
gwc.show();
FunWildCard1(gwc);
FunWildCard2(gwc);
FunWildCard3(gwc);
//使用通配符进行数据类型限定
Genwildcard<? extends Number> gw;
//当传入的数据类型不符合限定范围的时候就会报错
//gw = new Genwildcard<String>("123");
}
}
结果
java.lang.Integer
java.lang.Integer
java.lang.Integer
java.lang.Integer
用通配符限定上下边界
<? extends T>表示该通配符所代表的类型是T类型的子类。
<? super T>表示该通配符所代表的类型是T类型的父类。