黑马程序员_【总结】_ 高新_3_泛型和类型参数

本文详细解析了Java泛型的概念、用途及如何在集合、方法、类等不同场景下应用泛型和类型参数,包括泛型集合、泛型方法、泛型类、静态方法泛型、泛型接口等内容。同时,文章阐述了类型参数的类型推断过程,以及如何通过反射技术绕过泛型限制,将不同类型的元素添加到特定类型的集合中。此外,还介绍了泛型的细节、上限与下限的使用时机、类型参数的限制以及泛型接口的应用。最后,文章探讨了泛型的细节问题,如泛型的实际类型依赖于调用者、泛型类型的一致性、类型参数的多重限制等。
泛型和类型参数


---------- android培训 java培训、期待与您交流! ---------- 

---------------------------------------------------------------------------------------------------------------------------------------------
1、泛型  Generic
1、当操作的引用数据类型不确定的时,可用泛型,泛型可用于集合、方法、类等等多处
2、泛型是给编译器看的,运行时,没有泛型信息,也叫去泛型化
3、? extends E:可以接受E类型或者E的子类型 上限 <? extends E> 简单说限定在 E和E的子类
4、? super E:可以接受E或E的父类型 下限  <? super E>           简单说限定在E和E的父类
5、上限什么时候用:往集合中添加元素时
6、下限什么时候用:当从集合中获取元素进行操作的时候
7、熟练掌握HashMap 的两种取出方式


2、类型参数Type
1、编译器判断泛型的方法实际类型参数的过程称为类型的推断
2、ParameterizedType 该接口表示参数化类型,如 Collection<String>。 
3、Type[] Method.getGenericParameterTypes()  按顺序返回 Type 对象的数组
4、getRawType()  返回参数的原始类型
5、getActualTypeArguments()返回表示此类型实际类型参数的 Type 对象的数组。[0]数组中第一个
-------------------------------------------------------
【1】 
泛型  Generic
1、【泛型集合】
List<String> list = new ArrayList<String>();
2、【泛型方法】
将泛型定义到方法后,那么该方法就能操作不确定类型的方法。
      class Demo{  
             public <T> void show(T t){}
      }
3、【泛型类】
1、定义一个   Unknown 未知类型 的泛型 ,那么当实际调用的时候,这个Unknown 即可是String,也可以是int 等
2、泛型类的泛型在整个类中有效
3、如果方法中需要其他类型,则定义其他泛型即可。
	class Utils<Unknown>{
	    private Unknown u;
	    public void setObject(Unknown u);{
	        this.u = u;
	      }
	    public Unknown gerobject(){
	       return u;   
	     }
	}	

4、【静态方法泛型】
静态方法不可以访问类上定义的泛型,如果静态方法操作的数据类型不确定,可以将泛型定义在方法上。
       public static <W>  void menthod(W w){}
5、【泛型接口】
	interface Inter<T>{
	   void show(T t);
	}
	
	class InterImpl<R> implements Inter<R>{
	     public void show(R r) { 
	          System.out.println("show:"+r); 
	      }
	}
什么时候定义泛型类?
当类中操作的引用数据类型不确定的时候,

泛型的应用
public static void getGeneric()throws Exception
	{
		ArrayList<String> collection1 = new ArrayList<String>();  
		ArrayList<Integer> collection2 = new ArrayList<Integer>();   
		System.out.println(collection1.getClass()==collection2.getClass());   
		//collection2.add(“泛型为Ingeter,添加String ”);//不是int类型所以报错。   
		//通过反射:
		//获得collection2的Class字节码对象,获得叫"aad"的方法, 调用 该对象的构造函数 ,把String类型传递给collection2
		collection2.getClass().getMethod("add", Object.class).invoke(collection2, "泛型为Ingeter,添加String");   
		System.out.println(collection2.get(0)); //结果却真的给泛型为Ingeter的集合添加了String类型
	}
已经限制集合中元素的类型为Integer,可用反射却能将String存入,为什么? 
这是因为 【泛型是给编译器看的】,运行时就没有这些泛型信息了,这叫做“去泛型化”,
所以可以通过反射,获取集合字节码加入非指定的类型。


【通配符<?>】,也可以理解为占位符号
? extends E:可以接受E类型或者E的子类型 上限----- 简单说限定在 E和E的子类
? super E:可以接受E或E的父类型 下限 -------                 简单说限定在E和E的父类
(能写出HashMap两种取出方法,基本上说明掌握了泛型)
上限什么时候用:往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类型对象。
为什么?因为取的时候,E类型既可以接收E类对象,又可以接收E的子类型对象。?

下限什么时候用:当从集合中获取元素进行操作的时候,可以用当前元素的类型接收,也可以用当前元素的父类型接收。



泛型的细节:
 1、泛型到底代表什么类型取决于调用者传入的类型,如果没传,默认是Object类型;
 2、使用带泛型的类创建对象时,等式两边指定的泛型必须一致; 
原因:编译器检查对象调用方法时只看变量,然而程序运行期间调用方法时就要考虑对象具体类型了;
 3、等式两边可以在任意一边使用泛型,在另一边不使用(考虑向后兼容); 
       ArrayList<String> al = new ArrayList<Object>();  //错  //
要保证左右两边的泛型具体类型一致就可以了,这样不容易出错。 
       ArrayList<? extends Object> al = new ArrayList<String>(); al.add("aa");  //错  
            因为String是是子类,子类没有父类的方法,所以不能add
 4、一个类型参数可以具有多个限制   
       class C<T extends Comparable<? super T>&Serializable>      
5、类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以:
       GenericDao<String> dao = null;
       new genericDao<String>();
注意:
在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。
因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。


【类型参数的类型推断】(花了张老师两天的时间总结)
 编译器判断范型方法的实际类型参数的过程称为【类型推断】,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断, 具体规则如下

1当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,
那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,
即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
       swap(new String[3],3,4)   à    static <E> void swap(E[] a, int i, int j)  >> String类型
2.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,
如果调用方法时这多处的实际应用类型都对应
2-1【同一种类型】来确定,这很容易凭着感觉推断出来,例如:
add(3,5)   à static <T> T add(T a, T b)   >> Integer
2-2【不同的类型】[没有使用返回值],这时候取多个参数中的最大交集类型,
例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
	fill(new Integer[3],3.5f)   à static <T> void fill(T[] a, T v)    >>Ingeter 和浮点float
2-3【不同的类型】,[使用返回值],这时候优先考虑返回值的类型,
例如,下面语句实际对应的类型就是Integer了,编译将报告错误,
将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
       int x =(3,3.5f)   à static <T> T add(T a, T b) 
3.参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,
编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
      copy(new Integer[5],new String[5]) à static <T> void copy(T[] a,T[]  b);
      copy(new Vector<String>(), new Integer[5]) à static <T> void copy(Collection<T> a , T[] b);
【】HashMap 的两种取出方法。
public static void main(String [] args) throws Exception{
		//getType();
		HashMap<String,Integer> hm = new HashMap<String,Integer>();
		hm.put("xx", 24);
		hm.put("ff", 26);
		hm.put("ss", 25);
		showHMap1(hm);
		showHMap2(hm);
	}
	//把Key 作为元素,通过 keyset() 方法将HashMap 强转为 Set集合
	private static void showHMap1(HashMap<String,Integer> hm) {
		//Set<String> keyset = hm.keySet();
		for(Iterator<String> it = hm.keySet().iterator() ; it.hasNext() ; ){
			String key = it.next();
			Integer value = hm.get(key);
			System.out.println(key + "--" + value);
		}	
	}
	//把Entry<String, Integer>作为元素,通过 entry() 方法将HashMap 强转为 Set集合
	private static void showHMap2(HashMap<String,Integer> hm) {
		Set<Entry<String, Integer>> entry = hm.entrySet();
		for(Iterator<Entry<String,Integer>> it = entry.iterator() ; it.hasNext() ; ){
			Entry<String,Integer> en = it.next();
			String key = en.getKey();
			Integer value = en.getValue();
			System.out.println(key + ">>" + value);
		}
	}


---------------------------------
【2】
类型参数的类型推断   Type
编译器判断泛型的方法实际类型参数的过程称为类型的推断
其相对于知觉推断,其方法实现是一种非常复杂的过程(高难度问题)


通过反射获取泛型的实际类型参数
public class Typetest {
	//需求:获取Test 方法中泛型的参数类型
	public static void Test(Vector<Date> v){}
	public static void getType() throws Exception
	{
		//反射 Test 方法           类名                                                     方法名                                             
		Method method = Typetest.class.getMethod("Test",Vector.class); 
								//返回参数类型的数组						
		Type[] types = method.getGenericParameterTypes();
		//获取types  类型数组的第一个参数的类型
		ParameterizedType type =(ParameterizedType)types[0];
		System.out.println("返回参数原始类型:"+type.getRawType());
		System.out.println("[0]表示返回第一个参数的类型"+type.getActualTypeArguments()[0]);
	}
	public static void main(String [] args) throws Exception{
		getType();
	}
}
方法摘要:
ParameterizedType 该接口表示参数化类型,如 Collection<String>。 
Type[] Method.getGenericParameterTypes()  按顺序返回 Type 对象的数组
getRawType()  返回参数的原始类型
getActualTypeArguments()返回表示此类型实际类型参数的 Type 对象的数组。[0]数组中第一个







---------------------------------------------------------------------------------------------------------------------------------------------
---------- android培训、 java培训、期待与您交流!----------


----------------------------------------------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值