首先引入一个例子讨论
https://www.cnblogs.com/lwbqqyumidi/p/3837629.html
public class Test {
public static void main(String[] args) {
List list = new ArrayList();
list.add("qqyumidi");
list.add("corn");
list.add(100); // 整型类
for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); // 1
System.out.println("name:" + name);
}
}
}
定义了一个List类型的集合,先向其中加入了两个字符串类型的值,随后加入一个Integer类型的值。这是完全允许的,因为此时list默认的类型为Object类型(集合不记录对象的类型都变成了object类型)。编译阶段正常,而运行时会出现“java.lang.ClassCastException”异常。
在出现这个异常的原因是什么呢? 第三个是整数类型但进行了强制类型转换,发生异常
在如上的编码过程中,我们发现主要存在两个问题:
1.当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。不使用泛型则要强制转换一下类型
2.因此,//1处取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现“java.lang.ClassCastException”异常。
那么有没有什么办法可以使集合能够记住集合内元素各类型,那就可以输出的时候不强制转换类型?
还记得学c++的时候,它有一个模板类T
template
class test {
…
}
模板函数
template
int compare(const T& left, const T& right) {
if (left < right) {
return -1;
}
if (right < left) {
return 1;
}
return 0;
}
compare(1, 2); //使用模板函数
只要写一个模板,再传递一个参数类型就可以实现很多种不同类型的方法
java就借鉴了这种模板的方法,搞出了一个泛型
泛型的意思是:参数化类型,即把类型当成一个参数,是可以传递的
当定义一个集合的时候传递一个类型的参数,那就相当于这个集合已经确定只能放这个类型的元素了
比如 ArrayList persons = new ArrayList();
除了可以泛型集合以外还可以泛型接口、泛型类和泛型方法
定义一个泛型接口
public interface Generator<T> {
public T Test();
}
实现泛型接口
public class NumGenerator implements Generator<Integer> {
public Integer Test() {
.....
}
public static void main(String[] args) {
NumGenerator num = new NumGenerator();
.....;
}
}
指定自定义的类型
public class NumGenerator implements Generator<Person> {
public Person Test() {
.....
}
public static void main(String[] args) {
NumGenerator num = new NumGenerator();
.....;
}
}
定义一个泛型类--两个参数类型
class Person<K, V> {
private K age;
private V name;
public K getAge() {
return age;
}
public V setValue(V value) {
name = value;
}
public static void main(String[] args) {
Person<String, String> person = new Person<String, String>();
.....;
}
}
引用其他人写的,觉得不错:
定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。
Class<T>的作用就是指明泛型的具体类型,而Class<T>类型的变量c,可以用来创建泛型类的对象。
为什么要用变量c来创建对象呢?既然是泛型方法,就代表着我们不知道具体的类型是什么,也不知道构造方法如何,
因此没有办法去new一个对象,但可以利用变量c的newInstance方法去创建对象,也就是利用反射创建对象。
泛型方法要求的参数是Class<T>类型,而Class.forName()方法的返回值也是Class<T>,因此可以用Class.forName()作为参数。
其中,forName()方法中的参数是何种类型,返回的Class<T>就是何种类型。在本例中,forName()方法中传入的是User类的完整路径,
因此返回的是Class<User>类型的对象,因此调用泛型方法时,变量c的类型就是Class<User>,因此泛型方法中的泛型T就被指明为User,
因此变量obj的类型为User。
当然,泛型方法不是仅仅可以有一个参数Class<T>,可以根据需要添加其他参数。
为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,
如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。
public <T> void func(List<T> list, T t) {
list.add(t);
}
通配符?
现在引出一个问题?
如果有一个Fruit基类,其子类是Apple, Orange
public void print(ArrayList<Fruit> list) {
for (Fruit e : list) {
System.out.println(e);
}
ArrayList<Apple> list = new ArrayList<Apple>;
list.add(new Apple());
list.add(new Orange());
}
当调用print()函数时会出错
Apple是Fruit的子类,为什么会出错呢
原因是虽然Apple是Fruit的子类
但ArrayList<Apple>却不是ArrayList<Fruit>的子类
实际上他们是没什么关系的,不能转型
那要怎么办呢
这样就可以了
public void print(ArrayList<? extends Fruit> list) {
for (Fruit e : list) {
System.out.println(e);
}