深入理解java泛型

今天在这里我就说说java中的泛型,首先我们要先了解几个基本的问题

1.什么是泛型?为什么会出现泛型?

在说这个问题之前,我们先说一下List集合,我们在使用集合的时候,基本都会在后面加上一对尖括号,并在里面指定一个类型,例如List < String> ,表示这个集合中只能存放String类型的变量,这也是我们要说的泛型,在java5之前是并没有泛型的,在没有泛型的年代,如果我们要往List集合中存入元素的时候,无论你存入的是什么元素,List都会把它当成Object类型来处理,所以当从集合中取出元素的时候还需要进行类型的转换,这样会使代码臃肿且会降低性能,如果List集合中存入的不是同一种元素,还会造成ClassCastExeception异常。综上所述,泛型就是为了减去程序员编程出错而出现的一种方法。

2.泛型的使用

从字面上来看,泛型就是泛指一种类型,他的使用也是十分的灵活的,泛型可以使用在类,接口,方法,方法的形参等位置

public class Demo2 {
	public static void main(String[] args) {
		ArrayList list =new ArrayList();
		list.add("lex");
		//会出现编译错误
		list.(1);
	}
}

例如上面代码,集合中的泛型定义为String类型,如果试图将Integer类型的变量添加进去的话就会造成编译异常,会使编译无法通过,一定程度上保证了安全和代码的健壮性。

3.泛型中的继承问题

例如 C继承P ,C是P的子类,但List< C>并不是List< P>的子类,也就是说并不能用List< P>的类型来接收List< C>的类型变量

public class Demo2 {
	public static void main(String[] args) {
		List list =new ArrayList();
		//编译不通过
		//The method test(List) in the type Demo2 is not applicable for the arguments (List)
		test(list);
	}
	
	public static void test(List list){
		System.out.println("test");
	}
}
 
  

例如上面的代码,在该类中定义了一个静态的test方法,用来接收List< Object>的参数,我在调用该方法的时候传入了一个List< String >的集合,按照正常的习惯来看,String是Object的子类,所以用父类接收子类引用应该是可行的,但是编译器却报了参数类型不匹配的异常,也就是我们前面说的List< String >并不是List< Object>的子类。

4.通配符的使用

通配符的表示方法就是为一个问号?,例如List,这个可以用来表示各种List泛型的父类,这个List集合中的元素类型可以匹配任何的元素类型,需要注意的是List这个集合中无法调用add方法进行元素的添加,因为程序并没有办法确定这个List集合中的元素类型,根据前面的List< T>,他的add方法中的形参为add(T t),这里他的参数就是已知的,而使用通配符,无法知道元素的类型,所以没有办法添加元素,但是可以取出元素,其实在取出元素的时候,这个元素对程序来说仍然是未知的,但是无论他是什么类型,他一定会使Object的子类,所以get的返回值类型用Object来接受就没有问题了。

5.类型通配符的上限

在前面我们使用List可以代表任何泛型List的父类,但有些时候,我们并不希望这个List是任何泛型List的父类,我们只希望他是一部分类型的父类,这里我们就要使用到类型通配符的上限了,具体的使用方法为

public class Demo2 {
	public static void main(String[] args) {
	List list =new ArrayList();
	List list1 =new ArrayList();
	List list2 =new ArrayList();
	//编译出错 Type mismatch: cannot convert from ArrayList to List
	List list3 =new ArrayList();
	}	
}	

class A{
	
}

class B extends A{
	
}

class C extends A{
	
}

class D{
	
}

上面的代码,首先在外面定义了ABCD四个类,其中 BC是继承A类的 D类是单独的一个类,和ABC没有关系,在List集合中定义了一个上限,这个含义就是这个集合的泛型中只能传入A类或者是他的子类,上面分别将ABC传入,没有出现编译异常,当把D传入时出现了类型匹配失败的异常,这样也就约束了泛型中类的类型,而不是我们之前所说的传入所有泛型的集合了。这里的A就是类型通配符的上限。

而这种规定上限的集合同样是只能取不能添,因为这里虽然指定了上限,但是我们的程序仍然无法得知A类型的子类究竟是什么,仍然是未知的,所以和前面说的类型通配符的原理相同。

前面我们说到 B是A的子类,但List< B>并不是List< A>的子类,但是List< B>是List的子类,所以可以将List< B>类型的变量赋值给List的变量,这一过程被称作协变。

6.类型通配符的下限

类型通配符除了可以指定上限,还可以指定下限,写法,和上限相反,程序可以将List< Object>赋值给List,这一过程被成为逆变。

对于这种逆变得集合类型,程序只知道集合中的元素是下限的父类,但并不知道具体是哪一种类型,所以这种集合可以向其中添加元素,而取出的元素的类型仍为Object类型

你可能感兴趣的:(javaSE)