Java张孝祥视频 学习笔记 泛型

/*******************************************************************************************************************************/
此博客主要是在观看张孝祥老师的教学视频的过程中,自己所做的学习笔记,在此以博客的形式分享出来,方便大家学习,建议大家观看视频,然后以此笔记作为资料回顾复习。
参考资料
张孝祥2010年贺岁视频:Java高新技术   视频下载
疯狂Java讲义
java核心技术讲义
/*******************************************************************************************************************************/
为什么使用泛型
之所以使用泛型,很大程度上是为了让集合记住其中元素的数据类型类型,在泛型出现之前,一旦把一个对象丢进集合中,集合就会忘记对象的类型,把所有的对象当成object类型来进行处理。当程序从集合中取出对象后,就需要进行强制类型转换,这会让代码变得臃肿,同时类型转换的时候也有可能会出现,ClassCastException异常,使用了泛型之后,编译器在编译代码的时候,会进行类型检查,如果试图向集合中添加不满足类型要求的对象,编译器会提示类型错误,这提高了代码的安全性;其次使用泛型,可以提高代码的通用性
/*************************************** 37节视频 ********************************************************/
泛型是给编译器看的,让编译器挡住非法类型的输入,编译器编译带类型说明的集合,生成字节码前会去除掉“类型”信息,使程序的运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值跟原始类型相同。由于编译生成的字节码会去除掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型的集合中加入另一个类型的数据。

ArrayList<String> collection2 = new ArrayList<String>();
ArrayList<Integer> collection3 = new ArrayList<Integer>();
System.out.println(collection3.getClass() == collection2.getClass());//打印结果为 true    

collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");//通过反射将 String类型的数据加入到了 int集合中

泛型术语
整个称为ArrayList<E>泛型类型
ArrayList<E>  中E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或者实际类型参数
ArrayList<E>  中的,<>称为type of
ArrayList称为原始类型

参数化类型跟原始类型兼容    即:为了与JDK1.4兼容,泛型可以一边有约束,一边无约束
(1)参数化的类型可以引用一个原始类型的对象
 ArrayList<String> list = new ArrayList<>();
(2)原始类型可以引用一个参数化类型的对象
Collection c = new ArrayList<String>();

注:将参数化的类型转换成原始类型后,可能会发生类型错误,例如:
 ArrayList<String> strList = new ArrayList<>();
 List   list = strList;//ok
 list.add(1);// only a compile-time warning 不会提示错误,只会提示一个警告,但将list的数据取出进行强制类型转换的时候会出错
 String str = (String)list.get(0);//Error 报错

为了避免上述错误,可以用检查视图来检测这类问题

 ArrayList<String> strLista = new ArrayList<>();
 List<String> strListb = Collections.checkedList( strLista,String.class);
 List   list = strListb;//ok
 list.add(1);//Error 报错
 String str = (String)list.get(0);//



参数化类型不考虑类型继承关系  即:泛型二边要么都不使用约束,要么二边约束一致类型,或者一边有约束一边没有约束,同时二边必须使用引用类型(不能用基本类型实例化类型参数)
ArrayList<Integer>并不是ArrayList<Object>的子类,前者的不能赋值给后者
ArrayList<Object> obj = new ArrayList<Integer>;//这是错误

在创建数组实例时,数组的元素不能使用参数化的类型,可以声明这样的数组,但是实例化数组的时候会出错
ArrayList<String>[]  array = new ArrayList<String>[10];//这是错误

/*************************************** 38节视频 ********************************************************/
泛型中的通配符
使用?可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用参数无关的方法,不能调用参数化有关的方法
public static void printCollection(Collection<Object> c){
     for(Object obj :c){
        System.out.println(obj);
    }
    c.add("abc");//正确
    c  = new ArrayList<String>();//错误
}

public static void printCollection(Collection<?> c){
     for(Object obj :c){
        System.out.println(obj);
    }
    c.size();// 正确
    c.add("abc");//错误
    c  = new ArrayList<String>();//正确
}

类型通配符的扩展
ArrayList<? extends Number> 
ArrayList<? super   Integer>

/*************************************** 39节视频 ********************************************************/
   HashMap<String,Integer> maps = new HashMap<String, Integer>(); //泛型参数化,不能是基本类型
  maps.put("zxx", 28); //此处的28被自动装箱成Integer类型,
  maps.put("lhm", 35);
  maps.put("flx", 33);
 
  Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
  for(Map.Entry<String, Integer> entry : entrySet){
   System.out.println(entry.getKey() + ":" + entry.getValue());
  }

/***************************************** 40,41节视频 **************************************************/
自定义泛型方法
private static <T> T add(T x,T y){ 
  return null;
}

add(3,4);             T  ---Integer
add(3,3.5);          T  ---Number
add(3,"abc");      T  ---Object

T只能是引用类型,不能是基本类型,上面的基本类型被自动装箱成相应的引用类型

private static <T> void swap(T[] a,int i,int j){
  T tmp = a[i];
  a[i] = a[j];
  a[j] = tmp;
 }

swap(new String[]{"abc","xyz","itcast"},1,2); // 正确
swap(new int[]{1,3,5,4,5},3,4);// 错误

T只能是引用类型,不能是基本类型,此处无法进行装箱操作

T 可以添加限定,限定: 上限,下限,实现的接口

类型限定符的扩展
ArrayList<T extends Number>  //即使是接口 ,也用extends而不是implements
ArrayList<T super   Integer>

一个类型变量或者是通配符可以有很多个限定,例如
T extends Comparable&Serializable  
限定类型用&分隔,而逗号用来分隔类型变量
在Java的继承中,可以根据需要拥有多个接口超类型,但限定中至多只有一个类。如果用一个类作为限定,他必须是限定列表的第一个。

泛型代码和虚拟机

虚拟机没有泛型类型对象—所有对象都属于普通类。
无论何时定义一个泛型类型,都自动提供了一个相应的原始类型( raw type)。原始类型的  名字就是删去类型参数后的泛型类型名。 擦除( erased)类型变量,并替换为限定类型(无限  定的变量用Object)。
原始类型用第一个限定的类型变量来替换,如果没有给定限定就用Object替换。类  Pair<T>中的类型变量没有显式的限定,因此,原始类型用Object替换T,Pair<
T extends Number
>原始类型用
Number
替换T

翻译泛型表达式
当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换。例如,下面这个
     Pair<
Employee
> pair = new ...
     Emplotee employee = pair.getFirst();
语句序列
擦除getFirst的返回类型后将返回Object类型。编译器自动插入Employee的强制类型转换。也就  是说,编译器把这个方法调用翻译为两条虚拟机指令: 
    对原始方法Pair.getFirst的调用。
    将返回的Object类型强制转换为Employee类型。
当存取一个泛型域时也要插入强制类型转换。假设Pair类的first域和second域都是公有的  (也许这不是一种好的编程风格,但在Java中是合法的)。表达式: 
   Emplotee employee = pair.first;
也会在结果字节码中插入强制类型转换。

翻译泛型方法
类型擦除也会出现在泛型方法中。程序员通常认为下述的泛型方法
public static <T extends Number> T get()
是一个完整的方法族,而擦除类型之后,只剩下一个方法:
public static  Number  get()
注意,类型参数T已经被擦除了,只留下了限定类型Number。




练习
《一》
private static <T> T autoConvert(Object obj){
      return (T)obj;
 }
 Object obj = "abc";
 String x3 = autoConvert(obj);
《二》
 private static <T> void fillArray(T[] a,T obj){ 
      for(int i=0;i<a.length;i++){
           a[i] = obj;
      }
 }
《三》
public static <T> void printCollection(Collection<T> collection,T obj){ 
      System.out.println(collection.size()); 
      for(Object obj : collection){
           System.out.println(obj);
      }
      collection.add(obj);
 }

public static void printCollection(Collection<?> collection){ 
    System.out.println(collection.size()); 
    for(Object obj : collection){
       System.out.println(obj);
      }
 }

通配符方法比泛型方法更有效,此处利用泛型方法可以对集合添加元素

《四》类型推断
public static <T> void copy1(Collection<T> dest,T[] src){
 
 }
 
 public static <T> void copy2(T[] dest,T[] src){
 
 } 
copy1(new Vector<String>(),new String[10]); // 正确  此处T为String
copy2(new Date[10],new String[10]);//正确   此处T取Date跟String的交集
copy1(new Vector<Date>(),new String[10]); //错误  此处T的类型推断冲突

此处视频跳过了

/*************************************** 42节视频 ********************************************************/
定义泛型类型
如果类中有多个方法使用泛型,使用类级别的泛型
public class GenericDao <E>  {
     public void add(E x){
 
     }
 
     public E findById(int id){
             return null;
     }
 
     public void delete(E obj){
 
     }
 
     public void delete(int id){
 
     }
 
     public void update(E obj){
 
     }

     泛型类,可以看做普通类的工厂类
 
    当一个变量被声明为泛型时,只能被实例变量跟实例方法调用(也可以被内嵌类型调用),不能被静态变量跟静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有声明为泛型的类型参数  
    
     //此处错误
    public static  void update2(E obj){ 
 
     }
     //此处正确
     public static <E> void update2(E obj){ 
 
     }
 
     public E findByUserName(String name){
          return null;
     }
     public Set<E> findByConditions(String where){
          return null;
     }
}

CRUD 增删改查


/*************************************** 43节视频 ********************************************************/

通过反射获得泛型的实际类型参数

<一>通过反射获得方法的实际类型参数
 Class<?> clazz = ResolveData .class; //取得 Class
 Method method1 = clazz.getDeclaredMethod("applyCollection1", Collection.class,Map.class); //取得方法
 Type[] type1 = method1.getGenericParameterTypes();    //取得泛型类型参数的集合
           
for(int i=0;i<type1.length;i++){
            ParameterizedType ptype = (ParameterizedType)type1[i];//将其转成参数化类型,因为在方法中泛型是参数 
           Type str = ptype.getRawType(); //取得参数的实际类型
           System.out.println("方法参数:"+str);
           Type[] typeActual = ptype.getActualTypeArguments();
           for(int j=0;j<typeActual.length;j++){
                    System.out.println("泛型参数的实际类型:"+typeActual[j]);
            }
   } 
       
  Method method2 = clazz.getDeclaredMethod("applyCollection2",String.class,Integer.class); //取得方法 
 //参数里面如果不是参数化类型的话,那么 getGenericParameterTypes就返回与 getParameterTypes 一样 
 Type[] type2 = method2.getGenericParameterTypes();
 Type[] type3 = method2.getParameterTypes();      
 for(int i=0;i<type2.length;i++){
          System.out.println("getGenericParameterTypes方法获得的参数:"+type2[i]);
 }
for(int i=0;i<type2.length;i++){
         System.out.println("getParameterTypes方法获得的参数:"+type3[i]);
 }

 //声明一个空的方法,并将泛型用做为方法的参数类型
 public void applyCollection1(Collection<Number> collection,Map<String,Integer> msp){
       
 }
public void applyCollection2(String str,Integer inVal){ 
       
}

打印结果:

方法参数:interface java.util.Collection
泛型参数的实际类型:class java.lang.Number
方法参数:interface java.util.Map
泛型参数的实际类型:class java.lang.String
泛型参数的实际类型:class java.lang.Integer

getGenericParameterTypes方法获得的参数:class java.lang.String
getGenericParameterTypes方法获得的参数:class java.lang.Integer
getParameterTypes方法获得的参数:class java.lang.String
getParameterTypes方法获得的参数:class java.lang.Integer



整个java文件链接: http://pan.baidu.com/s/1gdmzPbd

<二>通过反射获得成员变量的数据类型

  private Map<String , Integer> score;
  Class<GenericTest> clazz = GenericTest.class;
  Field f = clazz.getDeclaredField("score");
  // 直接使用getType()取出Field类型只对普通类型的Field有效
  Class<?> a = f.getType();
  // 下面将看到仅输出java.util.Map
  System.out.println("score的类型是:" + a);
  // 获得Field实例f的泛型类型
  Type gType = f.getGenericType();
  // 如果gType类型是ParameterizedType对象
  if(gType instanceof ParameterizedType)
  {
   // 强制类型转换
   ParameterizedType pType = (ParameterizedType)gType;
   // 获取原始类型
   Type rType = pType.getRawType();
   System.out.println("原始类型是:" + rType);
   // 取得泛型类型的泛型参数
   Type[] tArgs = pType.getActualTypeArguments();
   System.out.println("泛型类型是:");
   for (int i = 0; i < tArgs.length; i++)
   {
    System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
   }

完整例成链接: http://pan.baidu.com/s/1hqtj48G

getType方法只能获得普通类型的Field的数据类型;对于增加了泛型参数的的Field,如果想要获取其中增加的泛型参数的类型,应该使用getGenericType()方法获得其类型


<三>通过反射获得父类的类的泛型参数的数据类型

public class Person<T> {

}

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Student extends Person<Student> {
    public static void main(String[] args) {
            Student st=new Student();
            Class clazz=st.getClass();
            //getSuperclass()获得该类的父类
            System.out.println(clazz.getSuperclass());
            //getGenericSuperclass() 获得带有泛型的父类
            //Type是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
            Type type=clazz.getGenericSuperclass();
            System.out.println(type);
            //ParameterizedType参数化类型,即泛型
            ParameterizedType p=(ParameterizedType)type;
            //getActualTypeArguments获取参数化类型的数组,泛型可能有多个
            Class c=(Class) p.getActualTypeArguments()[0];
            System.out.println(c);
        }
}

打印结果:

class com.test.Person
com.test.Person<com.test.Student>
class com.test.Student


运行时类型查询只适用于原始类型

虚拟机中的对象总有一个特定的非泛型类型。因此,所有的类型查询只产生原始类型。  例如,
List<String> list = new ArrayList<>();
if(list  instanceof ArrayList<Number>){
}
list  instanceof ArrayList<Number>返回的是true
实际上仅仅测试list是否是任意类型的一个ArrayList。下面的测试同样为真
if(list  instanceof ArrayList<T>){
}
或强制类型转换
List<Number>  listb = (ArrayList<Number>)list  //WARNING--can only test that list is a ArrayList
要记住这一风险,无论何时使用instanceof或涉及泛型类型的强制类型转换表达式都会看到  一个编译器警告。 
同样的道理, getClass方法总是返回原始类型。例如
List<String> lista = new ArrayList<>();
List<Number> listb = new ArrayList<>();
if(lista.getClass()==listb.getClass())// they are equal 
其比较的结果是true,这是因为两次调用getClass都将返回ArrayList.class。

重看 42 不能是静态类型
重看41类型推断
翻译泛型方法
Executors.newScheduledThreadPool(3);

不能抛出也不能捕获泛型类实例

不能抛出也不能捕获泛型类的对象。事实上,泛型类扩展Throwable都不合法。例如,下  面的定义将不会通过编译:
public class Problem<T> extends Exception{}//Error--can not extend Throwable

不能在catch子句中使用类型变量。例如,下面的方法将不能通过编译:
public static <T extends Throwable> void doWork(T t) 
{
        try{
        }
        catch(T e){ //Erroe--can not catch type variable
         
        }
}

但是,在异常声明中可以使用类型变量。下面这个方法是合法的:        ??????
public static <T extends Throwable> void doWork(T t) throws T//ok
{
        try{
        }
        catch(Throwable realCause){
            t.initCause(realCause);
            throw t;
        }
}

不能实例化类型变量

不能使用像new T(...), new T[...]或T.class这样的表达式中的类型变量。但是,可以通过反射调  用Class.newInstance方法来构造泛型对象。遗憾的是,细节有点复杂。具体参考java核心技术讲义。

泛型类的静态上下文中类型变量无效

不能在静态域或方法中引用类型变量。例如,下列高招将无法施展:
public static Singleton<T>{
    private static T singleInstance;//Error
    pubclic static T getSingleInstance(){Error
            if(singleInstance==null){
                    //construct new instance of T    
                    return singleInstance;
            }
    }

}
如果这个程序能够运行,就可以声明一个Singleton<Random>共享随机数生成器,声明一  个Singleton<JFileChooser>共享文件选择器对话框。但是,这个程序无法工作。类型擦除之后,  只剩下Singleton类,它只包含一个singleInstance域。因此,禁止使用带有类型变量的静态域和 方法

注意擦除后的冲突   ????????










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值