(46)Java基础 –泛型

  • Post author:
  • Post category:java



目录


一、泛型的概述


二、泛型类


三、泛型方法


四、泛型接口


五、泛型通配符


一、泛型的概述



定义

广泛的类型,在定义一个类的时候,类型中有些方法参数、返回值类型不确定,就使用一个符号,来表示那些尚未确定的类型,这个符号,就称为泛型。



使用

对于有泛型的类型,在这些类型后面跟上尖括号,尖括号里面写上泛型的确定类型(在使用某个类创建对象时,已经可以确定这个具体的类型了,那么就直接写出具体类型)。

例如:List<Integer> al = new ArrayList<Integer>();



泛型的好处

1、提高了数据的安全性,将运行时的问题,提前暴露在编译时期。

2、避免了强转的麻烦。



注意事项

1、前后一致:在创建对象时,赋值符号前面和后面的类型的泛型,必须一致。

2、泛型推断:如果前面的引用所属的类型已经写好了泛型,后面创建对象的类型就可以只写一个尖括号,尖括号中可以不写任何内容。<>特别像菱形,称为“菱形泛型”,jdk1.7特性。


(1)不使用泛型

public class Demo01_不使用泛型 {
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add(1);
		list.add("aaa");
		list.add(new Date());
		for(int i=0;i<list.size();i++){
			Integer x = (Integer)list.get(i);
		}
	}
}


(2)使用泛型

public class Demo02_使用泛型 {
	public static void main(String[] args) {
		List<String> list = new ArrayList();
		
		list.add("aaa");
		list.add("bbb");
		//list.add(111);
		for(String str:list){
		}
	}
}

二、泛型类


格式:

class 类名<泛型类型1,泛型类型2,…>{

}



说明

1、类名后面跟着的泛型类型,是泛型的声明,一旦泛型声明出来,就相当于这个类型成为了已知类型,这个类型就可以在整个类中使用

2、泛型的声明名称,只需要是一个合法的标识符即可,但是通常我们使用单个大写字母来表示,常用字母:T、W、Q、K、V、E

3、泛型确定的时机:将来在使用和这个类,创建对象的时候



需求:模拟ArrayList定义MyArrayList 完成add、get和size方法

public class MyArrayList<T> {

	private Object[] objs; //承装数据的数组
	private int size;  // 定义集合中元素的个数
	public MyArrayList(){
		objs = new Object[10]; // 数组初始化10个长度
	}
	
	public void add(T t){
		if(size>=objs.length){  // 集合中元素个数大于等于数组长度时,需要对数组进行扩容
		Object[] oldObjs = objs;  // 先把承装数据的数组赋给 oldObjs这个引用,用来保留原有数据,因为objs要指向新的数组
		objs = new Object[oldObjs.length+10];// 扩容10个长度
		System.arraycopy(oldObjs,0,objs,0,oldObjs.length); // 把原有数据拷贝回去
		}
			objs[size]=t;
		size++;
	}
	
	public int size(){
		return size;
	}
	
	public T get(int index){
		if(index>=size){
			throw new RuntimeException("没这么多元素");
		}
		return (T)objs[index];
	}
}

三、泛型方法


格式:

修饰符 <泛型声明1, 泛型声明2,…..> 返回值类型 方法名称(参数列表) {

}



说明

1、在方法上声明的泛型,可以在整个方法中,当做已知类型来使用

2、如果【非静态】方法上没有任何泛型的声明,那么可以使用类中定义的泛型

3、如果【静态】方法上没有任何的泛型声明,那么就不能使用泛型,连类中定义的泛型,也不能使用,因为类中的泛型需要在创建对象的时候才能确定。所以【静态】方法想使用泛型,就必须在自己的方法上单独声明。



需求:写一个方法,调用者传递什么类型的变量,该方法就返回什么类型的变量?

实现一:

由于无法确定具体传递什么类型的数据.那么方法的形参就定义为Object类型.返回值也就是Object类型.但是使用该方法时需要强制类型转换。

private Object getDate(Object obj) {
		return obj;
}

当不进行强制类型转换能否写出该功能?

目前所学的知识无法解决该问题

就需要使用泛型类解决

使用的泛型的自定义来解决以上问题。

public static void main(String[] args) {
	   int x = new Demo().getData(5);
       String str = new Demo().getData(“aaa”);
}
public <T> T getData(T data) {
		return data;
}

四、泛型接口


格式:

interface 接口名称<泛型类型1, 泛型类型2,…> {

}



说明

1、在接口声明上,定义好的泛型,可以在整个接口中当做已知类型来使用

2、泛型接口被其他类实现的时候,有两种实现方式:

1、声明的类不再是一个泛型类,而是一个确定了泛型的类,格式如下:

class 实现类类名 implements 接口名<具体类型> {

所有的泛型类型都已经被确定

}

2、声明的来还是一个泛型类,泛型的类型和接口的泛型一致,格式如下:

class 实现类类名<泛型标识符1> implements 接口名<泛型标识符1> {

所有的方法还都是可以使用泛型标识符的方法。

}

interface Inter<T> {
	void print(T t);
}
// 实现不知为何类型时可以这样定义
class MyInter<T> implements Inter<T> {
	public void print(T t) {
		System.out.println("myprint:" + t);
	}
}
//使用接口时明确具体类型。
class MyInter2 implements Inter<String> {
	@Override
	public void print(String t) {
		System.out.println("myprint:" + t);
	}
}

public static void main(String[] args) {
		MyInter<String> my = new MyInter<String>();
		my.print("泛型");
		MyInter2 my2 = new MyInter2();
		my.print("只能传字符串");
	}
}

五、泛型通配符

1、使用泛型的时候,没有使用具体的泛型声明T,而是使用了和声明过的某个泛型T有关的一类类型,就称为泛型的通配符。三种形式:


第一种形式

,使用?来表示可以是任意类型,例如:

Collection<E>接口中的removeAll(



Collection<?> c



),表示可以接收任意泛型类型的集合,作为该方法的实际参数,参数集合的泛型,可以是与E没有任何关系


第二种形式

,使用? extends E来表示必须是某个泛型类型或是该泛型类型的子类,例如:

Collection<E>接口中的addAll(



Collection<? extends E> c



),表示可以接收泛型类型是调用者泛型类型或者其子类的集合,作为该方法的实际参数。参数的泛型和调用者的泛型,必须有关(相同或者是子父类)。确定了泛型的上边界。


第三种形式

,使用? super E来表示必须是某个泛型类型或者是该泛型类型的父类,例如:

TreeSet<E>集合中,存储的都是E类型的元素,构造方法TreeSet(



Comparator<? super E>



com),表示可以接收泛型类型是集合元素类型或者是元素类型的父类的比较器,作为构造方法的参数。参数的泛型和集合的泛型,必须有关(相同或者是子父类)。确定了泛型的下边界。

public class Demo06_泛型通配符 {
	
	public static void main(String[] args) {
		Collection<String> coll = new ArrayList<String>();
		coll.add("xxxx");
		Collection<Integer> c = new ArrayList<>();
		coll.removeAll(c);  // removeAll(Collection<?> c) 表示传入的Collection对象的泛型是什么类型都可以
		// 如果 addAll(Collection<? extends E> c)  表示传入的Collection对象c的泛型只能是E或是E的子类(比如本例中E是String)
		// 如果 xxx(Collection<? super E> c) 表示传入的Collection对象c的泛型只能是E或是E的父类(比如本例中E是String)
		
	}
}

? extends E: 接收E类型或者E的子类型。

? super E: 接收E类型或者E的父类型。

正确:Vector<? extends Number> x = new Vector<Integer>();

错误:Vector<? extends Number> x = new Vector<String>();

上一篇:

(45)Java基础 –集合

下一篇:

(47)Java基础 –Map(映射)



版权声明:本文为kulr_w原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。