java基础之泛型

1 泛型的概念&为什么需要泛型

泛型泛化类型就是参数化类型,使原本固定的类型参数化,把类型的确定推迟到使用时才确定。

为什么需要泛型:

泛型的作用有两个:

一是起到约束的作用,定义泛型之后传入其他类型参数编译期就会报错,不会等到使用时发生类型转换错误(ClassCastException)

二是定义一次,多种类型都可以使用。

泛型中使用较多的是集合。

public static void main(String[] args) {

		//未指定泛型
		List list = new ArrayList();
		list.add("sldjflsf");
		list.add(1234);
		for (int i = 0; i < list.size(); i++) {
			//没有问题
			System.out.println(list.get(i)+"");
		}
		
		for (int i = 0; i < list.size(); i++) {
			//强制类型转换会报错
			System.out.println((String)list.get(i)+"");
		}
	}

result:

sldjflsf
1234
sldjflsf
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at com.ldx.extend.Demo5.main(Demo5.java:22)

定义泛型之后编辑器就会报错:

指定泛型类型之后,添加时不能添加指定类型以外的类型,强制类型转换时也就不会出错(其实也不会出现强制类型转换)

2 java中泛型使用方式

java中泛型的使用分成三类,泛型类,泛型接口,泛型方法。

/**
 * 泛型类的类型只能是类类型不能使普通类型
 *如果使用时,初始化不做任何限制,则可以传入任何的类型
 */
public class GenericDemo1 <T>{

	private T key;
	
	public GenericDemo1(T t) {
		this.key = t;
	}
//这是一个普通的方法并不是泛型方法
	public T getKey() {
		return key;
	}
}

interface GenerticInterface <T>{
	public T getData();
}

/**
 * 可以传入确定类型参数,但泛型将毫无意义
 * 未传入时在声明类的时候,需将泛型的声明也一起加到类中
 * 
 */
class GenerticDemo2<T> implements GenerticInterface<T>{

	@Override
	public T getData() {
		// TODO Auto-generated method stub
		return null;
	}
	
}

使用方式类似集合泛型使用,这里不再举例。泛型可以指定多个<K,V>

注意:

定义了泛型不一定非得使用,如果不使用任何类型参数都可以传入,但可能会发生类型转换错误

  • 泛型的类型参数只能是类类型,不能是简单类型。
  • 不能对确切的泛型类型使用instanceof操作。
  • if(instance instanceof GenericDemo<Number>){ }编译时就会出错。
  • 泛型可以用任意字符表示,但是最好是E,K,V,N,T,分别代表Element,Key,Value,Number,Type。

泛型方法:

public class Demo1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}
	
	//<T>是泛型方法的标识,表明该方法使用泛型类型T
	//泛型类中的没有<E>的方法不是泛型方法,
	//参数中使用泛型类的也不是泛型方法
	public <T> T getData(GenericDemo1<T> obj) {
		return obj.getKey();
	}

}

class GengerDemo3<T>{
	   //在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。
    public <E> void getData1(E t){
        System.out.println(t.toString());
    }

    //在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
    public <T> void getData2(T t){
        System.out.println(t.toString());
    }
    
    //传递的可变参数类型可以不一样
    public <T> void GetData3( T... args){
        for(T t : args){
            
        }
    }
    
    //这是错误的
    public static void  getData4(T data) {
    	
    }
    
     //这是正确的
     public void  getData5(T data) {
    	
    }
}

静态方法与泛型

静态方法要使用泛型,静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。

//会报Cannot make a static reference to the non-static type T 的错误提示
public class Demo4<T> {
	public T t;
	
	public static T staticMethod(T key) {
		return key;//无意义操作
	}
}

//正确的使用方法
public class Demo4<T> {
	public T t;
	
	public static<T> T staticMethod(T key) {
		return key;
	}
}

3 泛型通配符和类型的上下边界

通配符

泛型通配符用?表示,此处?是实参而不是形参,当我们只需要使用Object类的功能时就可以使用通配符。

 ArrayList<?> list;可以接收任意类型Arraylist,获取数据时是Object类型
 Class<?> clazz = Class.forName();

通配符?用在接收参数,方法中的形参用?修饰时可以传入的参数会受到限制。

泛型类型的上下边界:

? 通配符类型

<? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类

<? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object

上面使用了extends 和super关键字,泛型类型的上下边界也就是这两个关键字的作用。

当我们使用泛型并把泛型定义为T时,是没有办法使用T所代表的类型的函数的,泛型还可以对传入的泛型类型实参进行上下边界的限制,类型实参只准传入某种类型的父类或某种类型的子类。

3.1. “?”不能添加元素

以“?”声明的集合,不能往此集合中添加元素(但是可以接收现有子类型的赋值),所以它只能作为生产者(亦即它只能被迭代),如下:

public void get() {
//list1此时无法添加元素,获取元素也只能获取Object
		List<?> list1 = new ArrayList();
		//  通配符声明的集合,获取的元素都是Object类型
		List<String> list2 = new ArrayList();
		
		list2.add("aaaa");
		list2.add("aaab");
		list2.add("aaac");
		list2.add("aaad");
		list1 = list2;
		//  只能以Object迭代元素
		for(Object data: list1) {
		    System.out.println(data);
		}
	}

3.2 “? extends T”也不能添加元素

以“? extends T”声明的集合,不能往此集合中添加元素(可以接收现有子类型的赋值,可以添加null但是无意义),所以它也只能作为生产者,如下:

相对于以“?”声明的集合,“? extends T”能更轻松地迭代元素:

		List<? extends String> list3 = new ArrayList();
		list3 = list2;
//错误的无法添加元素只能获取元素
//		list3.add("alsdjlsf");
		for(String data:list3) {
			System.out.println(data);
		}

3.3 “? super T”能添加元素

只有“? super T”能添加元素,所以它能作为消费者。

List<? super String> list4 = new ArrayList();
		list4.add("sldjfsf");
		//错误无法确定类型
		/*for(String data : list4) {
			
		}*/
          for(Object data : list4) {
			
		}

针对采用“? super T”通配符的集合,对其遍历时需要多一次转型,如下:

//  只能获取到Object类型
for(Object data: list) {
    //  这里需要一次转型
    System.out.println(data);
}

4 泛型的擦除

java的泛型是伪泛型,是通过擦除实现的,java泛型在编译期有效,在运行期被删除,泛型参数类型在编译后都会被清除掉(其实并非全部)。

ArrayList<Integer>和ArrayList<String>被认为是不同的,但是对于编译后的代码,它们是完全相同的,只有ArrayList.class,没有ArrayList<Integer>.class和ArrayList<String>.class。

     类型擦除的基本过程先找到用来替换类型参数的具体类,一般是Object。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换成具体的类。同时去掉出现的类型声明,即去掉<>的内容。接下来就可能需要生成一些桥接方法(bridge method),这是由于擦除了类型之后的类可能缺少某些必须的方法。

关于泛型还有很多深入内容要研究,敬请期待后文。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值