泛型


    Java中集合类对于存入的元素会忘记数据类型,当需要使用时去除的元素变为Object类型。从Java5之后,Java引入参数化类型的概念,允许在创建集合的时候指定集合的元素类型,Java的参数化类型就叫做泛型。

创建这种特殊集合的方法是:在集合接口、类后增加尖括号,尖括号里放一个数据类型,即表明这个集合接口、集合类中只能保存特定类型的对象。

泛型使集合类的使用更加健壮,避免了程序将其他对象放入集合类中;并且程序会更加简洁,集合自动记住所有集合元素的数据类型,从而无需对集合元素进行强制类型转换。

不使用泛型时的代码:

<span style="font-size:14px;">import java.util.ArrayList;
import java.util.List;

class strList {
	private List strList = new ArrayList();

	public boolean add(String element) {
		return strList.add(element);
	}

	public String get(int index) {
		return (String) strList.get(index);
	}

	public int size() {
		return strList.size();
	}
}

public class Check {
	public static void main(String[] args) {
		strList stl = new strList();
		//手动实现编译检查
		stl.add("haha");
		stl.add("xixi");
		stl.add("huhu");
		System.out.println(stl);
		for (int i = 0; i < stl.size(); i++) {
			System.out.println(stl.get(i));
		}
	}

}</span>

Java7之后允许在构造器后不需要带完整的泛型信息,Java可以推断等号右边的尖括号内的泛型信息。

当采用泛型之后的代码:

<span style="font-size:14px;">import java.util.ArrayList;
import java.util.List;

public class ListTest {

	public static void main(String[] args) {
		List<String> strList = new ArrayList<>();
		strList.add("haha");
		strList.add("xixi");
		strList.add("huhu");
		for( int i = 0 ; i < strList.size() ; i++)
		{
			System.out.println(strList.get(i));
		}
	}

}</span>

一、深入泛型

需要说明的是,泛型并不是只用于集合类,我们可以为任意类添加泛型声明。

泛型的实质是允许在定义接口、类时声明类型形参,类型形参在整个接口、类体内可以当成类型使用,几乎所有可使用普通类型的地方都可以使用这种类型形参。

<span style="font-size:14px;">public class Shape<T> {
	private T dataT;

	public Shape() {

	}

	public Shape(T element) {
		dataT = element;
	}

	public void setData(T element) {
		dataT = element;
	}
	
	public T getData()
	{
		return dataT;
	}

	public static void main(String [] args )
	{
		Shape<String> sh = new Shape<>("圆形");
		System.out.println(sh.getData());
		Shape<Integer> sh2 = new Shape<>(2);
		System.out.println(sh2.getData());
	}
}</span>

程序中定义一个List<E>接口,实际使用是可以产生无数多个List接口,只要E传入的参数不同,系统将会产生一种新的List子接口。需要说明的是系统并没有进行源代码的复制,这是一种动态生成的逻辑上的子类,物理存储上是不存在的。

当创建了具有泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类派生子类。但需要指出的是,当使用了这种接口或父类时将不能包含类型形参。

<span style="font-size:14px;">public class cirle extends Shape<String></span>

泛型的类型参数不管是哪一种,对于Java来说,他们依然是一个相同的类,占用一块相同的内存空间。所以说在类的定义中,静态方法、静态初始化块或者静态变量的声明不允许使用泛型进行定义。

二、关于类型通配符

如果说ZI是FU的一个子类型,而Method是一个具有泛型声明的类或者方法,Method(FU)并不是Method(ZI)的父类,但对于FU[]来说它依然是ZI[]的父类。这是比较奇特的一点。

Java泛型的设计原则是,只要代码在编译是没有出现警告,就不会运行时出现异常,泛型的异常检测是Check检测,并不是运行时检测。

为了表示各种泛型的父类,在尖括号内使用类型通配符“?”,它可以匹配任何类型.

<span style="font-size:14px;">      public void text( List<?> list)
	{
		for( int i = 0 ; i < list.size(); i++ )
		{
			System.out.println(list.get(i));
		}
      }</span>

泛型类中实现参数化类型的方法,接受的元素必须是指定参数化类型的类的对象或子类的对象。

A、关于类型通配符的上限

当使用诸如List<?>这种形式时,即表明这个List集合可以是任何泛型List的父类。但是说我们并不想使这个List<?>成为泛型List的父类,只是想让它成为某一类泛型List的父类。

List< ? Extends shape >是一个有通配符上限的泛型集合,此处的?代表一个未知类型,但这个未知类型一定是Shape的子类型。

与类同时继承父类、实现接口类似的是,为类型形参指定多个上限时,所有的接口上限必须位于类上限之后。也就是说,如果需要为类型形参指定类上限,类上限必须位于第一位。

一般用于存储对象的时候。

B、关于类型通配符的下限

Java允许设置通配符下限:< ? Super Type >,这个通配符表示必须是Type本身,或者是Type的父类。

一般用于读取对象时的时候。

三、泛型方法

所谓泛型方法就是在定义方法时定义一个或多个类型形参。

<span style="font-size:14px;">import java.util.ArrayList;
import java.util.Collection;

public class GenericMethod {

	static <T> void Array2Collection(T arry[], Collection<T> coll) {
		for (T key : arry) {
			coll.add(key);
		}
	}

	public static void main(String[] args) {
		Object [] oaObjects = new Object[100];
		Collection<Object> co = new ArrayList();
		Array2Collection(oaObjects, co);
	}
}</span>

上面程序中定义的泛型方法中定义了一个T类型形参,这个类型形参可以在该方法中当成普通类型使用,但是这种形参只能在该方法中使用,同理接口、类声明的类型形参只可以在接口、类中使用。

四、类型擦除与转换

  在严格的泛型代码里,带泛型参数的类总应该带着类型参数,但Java也允许在私用泛型声明的类时不指定实际的类型参数。如果没有为这个泛型类指定实际的类型参数,则该类型参数将采用原始类型,默认是声明该参数时指定的第一个参数上限。

<span style="font-size:14px;">public class Shape<T extends Number> {
	private T dataT;
	public Shape() {

	}
	public Shape(T element) {
		dataT = element;
	}

	public void setData(T element) {
		dataT = element;
	}
	
	public T getData()
	{
		return dataT;
	}
	public static void main(String [] args )
	{
		Shape<Integer> sh = new Shape<>(2);
		int data = sh.getData();
		Shape sh2 = sh;
		Number num = sh2.getData();
		System.out.println("data = " + data);
		System.out.println("num  = " + num);
	}
}
  </span>
五、数组与泛型的比较
  数组是协变的也就是说子类的数组类型也是父类的数组类型。然而泛型中却不存在这种问题,也就说在泛型中子类Type1的Collection<Type1>对象既不是父类Type2的Collection<Type2>的子类型,也不是父类Type2的Collection<Type2>的类型,保证了数据的安全性。这是数组的内在缺陷,是危险的。
  数组是具体化的,只有当数组运行的时候才会知道数组的数据类型,数组对其中的数据类型检查相对滞后。而泛型中存在擦除现象,泛型中存储的数据在运行时均被看做是Object类型,在变一阶段进行检测。
  尤其需要注意的是反省数组是不存在的,这是一种非法形式。这是一种类型不安全的表现形式,而且数组违反了泛型系统的数据保证。

  因此应该优先使用泛型,例如Collection类,通过类型参数而不是Object类型有更好的实用性。当然采用类型参数定义的类会出现警告,但是我们可以自己证明程序的安全性,并在尽量小的范围内禁用这些警告。总而言之,使用泛型比在应用该类的方法时的类型转换更加安全,使用更加容易。

        我用数组自己实现的List,代码如下:

<span style="font-size:14px;">import java.util.Arrays;

public class SequenceList<T> {
	// 默认数组长度
	private int DEFAULT_SIZE = 16;
	// 保存数组长度
	private int capacity;
	// 用于保存线性表的数组
	private Object[] elementData;
	// 当前存储数据个数
	private int size = 0;

	// 以默认长度创建存储数组
	public SequenceList() {
		elementData = new Object[DEFAULT_SIZE];
		capacity = DEFAULT_SIZE;
	}

	// 存储数组长度不足时,数组拓展
	private void ensure(int oldsize) {
		if (oldsize > capacity) {
			while (capacity < oldsize) {
				capacity <<= 1;
			}
			elementData = Arrays.copyOf(elementData, capacity);
		}
	}

	// 以某元素初始化存储数组
	public SequenceList(T elemenT) {
		this();
		elementData[0] = elemenT;
		size++;
	}

	// 以某元素及指定长度初始化存储数组
	public SequenceList(T element, int initsize) {
		capacity = initsize;
		elementData = new Object[initsize];
		elementData[0] = element;
		size++;
	}

	// 获取线性表长度
	public int length() {
		return size;
	}

	// 获取线性表是否为空
	public boolean empty() {
		return size == 0;
	}

	// 获取指定索引的元素
	public T get(int i) {
		if (i > size - 1 || i < 0) {
			throw new RuntimeException("索引越界");
		}
		return (T)elementData[i];
	}

	// 查找线性表中的元素位置
	public int contains(T key) {
		for (int i = 0; i < size; i++) {
			if (elementData[i].equals(key)) {
				return i;
			}
		}
		return -1;
	}

	// 向线性表中添加元素
	public void add(T element) {
		Insert(element, size);
	}

	// 想存储数组中指定位置插入元素
	public void Insert(T element, int index) {
		if (index > size || index < 0) {
			throw new IndexOutOfBoundsException("不存在该位置");
		}
		ensure( size + 1);
		System.arraycopy(element, index, element, index + 1, size - index);
		elementData[index] = element;
		size++;
	}

	// 删除最后一个元素
	public T remove() {
		return delete(size - 1);

	}

	// 删除指定位置元素
	public T delete(int index) {
		if (index < 0 || index > size - 1) {
			throw new IndexOutOfBoundsException("线性表越界");
		}
		T oldValue = (T)elementData[index];
		elementData[index] = null;
		int key = size - index - 1;
		if (key > 0) {
			System.arraycopy(elementData, index + 1, elementData, index, key);
		}
		return oldValue;
	}

	// 线性表清空
	public void clear() {
		Arrays.fill(elementData, null);

	}

	// 输出
	public String toString() {
		if (size == 0) {
			return null;
		} else {
			StringBuilder str = new StringBuilder();
			str.append("[");
			for (int i = 0; i < size; i++) {
				str.append(elementData[i] + ",");
			}
			str.append("]");
			return str.toString();
		}
	}
}
</span>



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值