泛型
所谓泛型就是在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态的指定。虽然在使用集合类,比如:List、Set等类时,会使用泛型,但是根据定义并不是只针对集合,在定义类的时候就可以指定泛型。
public class Person<T> {
private T info;
public Person(T info) {
this.info = info;
}
}
Person<String> person = new Person<>("name");
当创建了带泛型声明的接口、父类的时候,可以为该类接口创建实现类、或从该父类派生出子类。但需要注意的是,当使用这些泛型接口、父类的时候不能再包含类型形参。例如下面是一种错误的描述:
// 这是一种错误的描述
public class Student extend Person<T> {
}
可以使用如下的方式来继承该类:
public class Student extend Person<String> {
}
或者不指定泛型类型,但是这样会有警告信息:
public class Student extend Person {
}
有的时候我们在程序中需要使用泛型的父类,这个时候可以使用如下方式实现:
// 这种用法有警告信息
public void test(List list) {
....
}
这种方式是正确的,但是使用这种方式可能会引起警告信息,如果采用下面的方式来使用就会引起错误:
// 这种用法是错误的
public void test(List<Object> list) {
...
}
在Java中为了表示泛型类的父类,需要使用类型通配符,就是用?来表示。在上面的例子中可以使用如下的形式来表示:
// 这种表示方式表示所有List类的父类,没有错误也没有警告信息
public void test(List<?> list) {
...
}
但是这里存在一个问题,如果使用List<?>这种父类,在程序中使用的话还需要强制类型的转换,转换成需要的类型,例如:
public void test(List<?> list) {
for (Object obj : list) {
String str = (String)obj;
...
}
}
这种使用方式还需要把对象强制类型转换一下,为了解决这种问题,可以给类型通配符设定一个上线:
public void test(List<? extends xxx> list) {
...
}
这种方式就可以把List中的对象当成xxx类型,直接访问。
在定义泛型类的时候并不希望这个泛型类型是随意添加的,需要指定该类型是某个类型的子类,这个时候需要指定泛型类型的上限。
为了解决这个问题需要指定类型形参的上限,例子如下:
public class Question<T extends Person> {
...
}
这个时候在使用类型T的时候就必须传入Person的子类。
泛型方法
泛型方法的签名如下:
修饰符 <T, S> 返回值类型 方法名(形参列表) {
...
}
T 和 S 都可以使用这种形式: T extends E,表示E是T的上限。
// 例子如下:
public <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o);
}
}
如果可以使用泛型方法,那泛型方法和类型通配符之间存在怎样的区别:在大多数的情况下都可以使用泛型方法来代替类型通配符。通配符就是被设计用来支持灵活的子类化的,但是泛型方法就是允许类型形参被表示方法的一个或者多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系。