泛型在java中非常重要,很多地方都大量的用到了。
集合中泛型的运用:
添加类型不受约束,取出的元素不知道改用什么类型的引用去指向。
于是引入了泛型来做约束:
泛型的应用场合
1)当集合需要约束类型的时候
2)当我们不知道参数类型但是有需要定义方法的时候
泛型的分类
1泛型类
最典型的就是各种容器类,如:List、Set、Map。可以取看看这些集合的源码
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; private static final int DEFAULT_CAPACITY = 10; private static final Object[] EMPTY_ELEMENTDATA = {};
此处省略一万航/
}
class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
private 泛型标识 /*(成员变量类型)*/ var;
.....
}
}
public class Demo1 { public static void main(String[] args) { Generic<String> generic=new Generic<String>(); generic.setKey("小红"); System.out.println(generic.getKey()); Generic<Date> generic2=new Generic<Date>(); generic2.setKey(new Date()); System.out.println(generic2.getKey().getTime()); } } class Generic<T>{ private T key; public T getKey() { return key; } public void setKey(T key) { this.key = key; } }泛型类定义后他的泛型是在new操作的时候传入的类型决定
那么泛型类定义后new操作一定要传入泛型实参吗?我们使用List的时候有规定必须传入泛型实参吗?没有。
就是说:泛型实参传入后会分局传入的泛型实参做响应的限制。此时泛型才会起到限制作用。
如果不传入的话反省类中的泛型方法或成员变量定义的类型可以为任何类型
看到了吧 如果不指定类的泛型 那么你在get和set的时候是可以任意指定类型的,
泛型接口和泛型类大体相同。
interface Generator<T>{ public T next(); } class FoodGenerator<T> implements Generator<T>{ @Override public T next() { return null; } }如果FoodGenerator不指定<T>编译器会报错,因为你实现了接口,必然实现接口的方法。而接口是定义了泛型的。所以必须指定否则编译报错
如果类设计时指定了接口的泛型实参,则:
interface Generator<T>{ public T next(); } class FoodGenerator implements Generator<String>{ @Override public String next() { return null; } }
3泛型通配符
class A{ int a; } class B extends A{ int b; } class C extends B{ int c; } class D extends C{ int d; } class E extends D{ int e; }
C类是B类的子类,在使用Generator<B>作为形参的方法中,能否使用Generic<C>
的实例传入呢?
public class Demo1 { public static void main(String[] args) { print(new Generator<B>() ); } public static void print(Generator<B> generator){ System.out.println(generator.toString()); } }
编译器就为我们报错了
看来Generator<C>无法被当做Generator<B>的子类传如方法
同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的
如果将方法print改一下呢?
编译报错消失并且可以正常运行了
这里的?是实参哦,不是形参,请注意!
这个?是一个通配符。当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
通配符边界:其中<? extends Number>表示通配符的下边界,即“?”只能被赋值为Number或其子类型。其中<? super Integer>表示通配符的下边界,即“?”只能被赋值为Integer或其父类型。
4泛型方法
万变不离其宗,我们来看看这句话,然后写demo去验证这句话:
泛型类,在实例化类的时候知名泛型的具体类型。
泛型方法,是在调用方法的时候知名泛型的具体类型
复用上面定义的类
class A{ int a; } class B extends A{ int b; } class C extends B{ int c; } class D extends C{ int d; } class E extends D{ int e; }
修改泛型类
interface Function<T>{ T print(); } class Generator<T> implements Function<T>{ @Override public T print() { return null; } public <T> T getGenerator(Class<T> clazz) throws IllegalAccessException,InstantiationException { T instance=clazz.newInstance(); return instance; } }
泛型类中定义了泛型方法。然后运行:
可以看到,类的泛型定义在new操作的时候指定了是String,但是并不影响泛型方法,泛型方法参数我们传入了C类,那么返回值也是C类。
应证了:
1)泛型方法是我们不知道传入参数类型的时候定义的
2)泛型方法是调用的时候确定类型的
3)泛型类是new操作的时候确定类型的
public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T
与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
方法中的T和类上的T效果不一样,方法的T也可以写作A,B,C,D,E等等
4.1泛型方法和可变参数
4.2静态方法与泛型
静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。
总结
泛型的基本使用就是以上的了,有个别细节需要注意的这里就不在深入了,编译器爆红结合以上的记录其实足以充分正确的使用泛型了。