Java中泛型的?和T的区别

前言:

jdk5.0后java提供了泛型,用大写字母表示,一般用T,表示是一个不确定的具体的类型。
?表示是一群不确定的具体的类型,但?是它们的父类。

正文:?和T的区别

T:代表一种类型。
?:通配符,泛指所有的类型,是所有类型的父类。

用法:

一般而言,T主要用于泛型类的定义、泛型方法的定义,还有具体的变量的类型定义上,如:
定义泛型类:

class Super{}

定义泛型方法:

public  void testMethod(T[] a, Collection c)

修饰泛型类的属性成员:

class Sub{
	T temp;
}

以上三种情况,在实际使用中,都要求是指明了具体的泛型类型,才可以使用。而"?“是通配符,代表的是不确定的类,是一群类的父类,所以以上三种情况都不可以用”?"代替T。

那么,通配符,一般是用于定义一个引用变量,以便实现“多态”调用(非真正意义上的多态)
比如定义一个sup的引用变量,不指定具体的泛型类型,而用通配符表示。如下:

Super sup = null;
sup = new Super();
sup = new Super();
......

也可以做方法的形参,定义引用变量,同样是为了实现“多态”调用:

public static void main(String[] args) {
	List l1 = new ArrayList();
	List l2 = new ArrayList();
	l1.add("AAA");
	l2.add(15);
	read(l1);
	read(l2);
}
public static void read(List list) {
	for (Object o : list) {
		System.out.println(o);
	}
}

由于泛型在继承上的体现,我们知道,如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G< B >并不是G< A >的子类型!所以,无法通过多态的方式声明一个引用变量指向多个子类的实例。如下:

Super sup1 = new Super();
Super sup2 = new Super();
Super sup3 = new Super();

同一个类,如果泛型的具体类型不同,定义引用变量在不使用通配符的情况下,需要为每一个具体的实例创建对应的引用变量。
为了弥补这一缺点,通配符出现了,如果利用通配符,那么我们就可以如下声明:

Super sup = null;
sup = new Super();
sup = new Super();

从这也可以看出,通配符是没有指代具体的类型的,是多个类型的父类。

补充:

//以下是错误用法:
//编译错误:通配符不能用在创建对象上,右边属于创建集合对象
ArrayList list2 = new ArrayList();

有限制通配符:

? extends A:
G 可以作为G< A >和G< B >的父类,其中B是A的子类

? super A:
G 可以作为G< A >和G< B >的父类,其中B是A的父类

T extends A 和 T super A:效果同上,唯一的差别还是在?和 T上(即上文所比较的内容)。

无限制通配符和有限制通配符的数据读写问题

无限制通配符读写:

:可以读,尽管被无限制通配符修饰,但不管如何,容器里的元素永远是一个对象,也就是一个Object,所以可以用Object o这个引用变量来读取容器元素,即利用对象多态性。

:不可以往容器里写入元素,因为写入的元素与容器的具体类型的关系不明确。比如:

ArrayList list = new ArrayList();
list = new ArrayList();

list可以指向不同的具体容器,相对应的能接收的元素类型也跟着改变。因此往容器里添加数据时,会因为无法确定所能添加的具体元素类型为何,导致的类型不安全而编译不通过。

注意:可以添加null,因为null是所有类型的成员

有限制通配符读写:

① ? extends A:

:往容器中添加的元素类型,一定是A或A的子类(如果是接口,则为实现类),因此读取出来的数据,一定可以用A类型来做引用(当然Object也可以)。

:由于容器的具体类型未知,如果往容器添加元素,无法确保添加进去的具体数据是该容器具体类型的子类还是父类,因此存在类型不安全问题,所以是不允许往里添加数据的

举个栗子
例如:有三个类,Creature,Person,Student,其中,Student extends Person,Person extends Creature。

那么,现在声明:List list;

如果,此时实例化:list = new ArrayList< Student >();

再往其中添加数据时,根据list的声明,是可以往其中添加Creature及其子类的实例化对象的,但由于list指向的具体容器为Student类型,根据对象多态性,子类引用不可以指向父类对象,因此实际容器是无法承装Creature和Person类的实例对象的,从而会引起类型不安全的矛盾。因此,是不允许往容器里写入数据的。

注意:null仍然可以。

②? super A:

:不管容器里添加的元素是A还是A的父类的实例对象,它们始终都是Object对象,因此可以读取,用Object类型来做引用(不能用A类型来做引用)。

只能写入A类型及其子类的实例对象。因为具体容器的类型最低等级是到A,所以不管容器具体类型为何,它都能保证>= A,所以A的实例化对象可以被写入(对象多态性),当然,既然作为父类的A可以被写入,那么A的子类自然而然也可以被写入容器里了。

注意:null仍然可以。

以上纯属个人学习见解,如有错误还望海涵。

你可能感兴趣的:(java,多态,泛型)