黑马程序员:jdk1.5新特性4(注解泛型)

来源:互联网 发布:java 员工管理系统 编辑:程序博客网 时间:2024/06/15 22:34
 

重点:
注解
了解注解及java提供的几个基本注解(jdk中java.lang包提供了最基本的annotation)
     注解:相当于一种标记,加了注解就等于打上了某种标记,没加,则等于没有某种标记。以后,

java编译器开发工具和其他程序可以用反射来了解你的类及各种元素上有无标记,有什么标记,就去做相

应的事。标记可以加在包、类。字段、方法、方法的参数及局部变量上。

 @Deprecated  :过时的程序元素
 @Override     :重写
 @SuppressWarnings:指示应该在注释元素中取消显示指定的编译器警告。


自定义注解及其应用
 1 @Retention 元注解 (Enum 类型)
  取值:RetentionPolicy.SOURCE、RetentionPolicy.CLASS、Retentionpolicy.RUNTIME
  对应:java源文件              class文件                内存中字节码
 2 @Target 元注解(Enum 类型)
  取值:ElementType.METHOD、ElementType.TYPE...
  设置Taget时 可用数组方法{ElementType.TYPE,ElementType.METHOD}
 3 元注解及其枚举属性值不用用记,只要会看JDK或java.lang.annotation包下的类

 

为注解增加基本属性
 1 什么是注解的属性
   一个注解相当于一个胸牌,如果你贴了胸牌,就是传智博客的学生,否则就不是。如果还想区分是哪

个班的学生,这时候可以为胸牌增加一个属性来区分,增加了属性的效果@MyAnnotation(color="red")
 2 定义基本类型的属性和应用属性
   在注解中增加String color();
   应用:   @MyAnnotation(color="red");
 3 用反射的方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
   MyAnnotation a=(MyAnnotation)AnnotationTest.calss.getAnnotation(MyAnnotation.class);
   System.out.println(a.color());
   可以认为上面这个@MyAnnotation是MyAnnotation类的一个实例对象
 4 为属性指定缺省值:
   String color() default "yellow";
 5 value属性:
    String value() default "zxx";
    如果注释中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只

有一个value属性),那么可以省略value=这部分,例如:@MyAnnotation("lhm")

 为注解增加高级属性
1 数组类型的属性
 int[] arrayAttr()  default{1,3,6};
 @MyAnnotation(arrayAttr={7,4,8})
 如果数组属性中只有一个元素,这时候属性值部分可以省略大括号
2 枚举类型的属性
  EnumTest.TrafficLamp lamp();
  @MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)
3注解类型的属性:
  设置MetaAnnotation annotationAttr() default @MetaAnnotation("xxx");
  应用:@MyAnnotation ( annotationAttr  = @ MetaAnnotation("yyy"))

 

重点:泛型(最难掌握的部分)
我们在实际应用中 不能掌握的那么深入,掌握泛型的一些基本内容就可以了
没有泛型时只要是对象,不管什么类型都可以存放在同一个集合中(但编译器会报告unchecked警告),

使用了泛型就可以将一个集合的元素限定为一个特定类型,集合中只能存储同一类型的对象,这样更安全

;并且从集合获取对象时,编译器也可以知道是什么类型的对象,不需要强制类型转换。
ArrayList<String> collection=new ArrayList<String>();
 collection.add("abc");
 /*collection.add(1);
 collection.add(1l);*/ 这两行代码编译时就会报告语法错误
 String retval=collection.get(0);// 不需要类型转换
泛型时给javac编译器使用的,限定集合中的输入类型,让编译器挡住非法输入,编译器编译带类型说明

的集合时 会去掉“类型”信息,是程序运行效率不受影响,对于参数化得泛型类型,getClass()方法返

回值和原始类型完全一样。由于编译产生的字节码会去掉泛型类型信息,只要跳过编译器,就可以往某个

泛型集合中加入其它类型的数据,例如,用反射得到集合,在调用add方法即可。

了解泛型
1 ArrayList<E>类 定义和ArrayList<Integer>类引用中涉及如下术语:
 <1 整个称为ArrayList<E>泛型类型
 <2 ArrayList<E> 中的E 称为类型变量或类型参数
 <3 整个ArrayList<Integer>称为参数化的类型
 <4 ArrayList<Ingeger>中的Integer称为类型参数的实例或实际类型参数
 <5 ArrayList<integer>中的<>称为typeof
 <6 ArrayList称为原始类型
2 参数化类型与原始类型的兼容性
 参数化类型可以引用一个原始类型的对象,编译报告警告,
  例如:Collection<String> c=new Vector();
 原始类型可以引用一个参数化类型的对象,编译报告警告,
  例如:Collection c=new Vector<String>();
3参数化类型不考虑类型参数的继承关系
 Vector<String> v=new Vector<Object>();  错误!
 Vector<Object> v=new Vector<String>();  错误!
4在创建数组实例时,数组的元素不能使用参数化的类型,下面的语句有错误:
 例如:Vector<Integer> vectorList[] =new Vector<Integer>[10];
5思考下面的代码有错误吗?
  Vector v1=new Vector<String>();  正确
  Vector<Object> v=v1;             正确
      因为编译器只会一条条的读代码,这2段代码符合参数化类型与原始类型的兼容性。
 
泛型中的?通配符
1问题:定义一个方法该方法用于打印出任意参数化类型的集合中的所有数据,怎么定义
 错误方式:这个方法无法调用,因为调用任何类型为参数都与Object不符
   public static void printCollection(Collection<Object> cols) {
    for(Object obj: cols){
         System,out.println(obj);
 }
   /*cols.add("string"); //没错
    cols=new HashSet<Date>();  //会报告错误*/
 }
正确方式:
public static void printCollection(Collection<?> cols) {
       for(Object obj: cols){
            System.out.println(obj);
    }
     // cols.add("string"); //错误因为它不知自己将来匹配的一定是String
       cols.size();  //正确,这方法与类型参数无关
       cols=new HashSet<Date>();  //正确 只是个=号赋值
    }
总结: 使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用于引用,可以调用与

参数无关的方法,不能调用与参数有关的方法。


泛型中的?通配符的扩展
1 限定通配符的上边界:
 正确Vector<?extends Number> x=new Vector<Integer>();
 错误Vector<?extends Number> x=new Vector<String>();
2 限定通配符的下边界:
 正确Vector <?super Integer> x=new Vector<Number>();
 错误Vector <?super Integer> x=new Vector<Byte>();
3 提示:限定通配符总是包括自己
 
泛型集合的综合应用案例:
  HashMap<String,Integer> maps=new HashMap<String, Integer>();
  maps.put("zll", 14);
  maps.put("smm",15);
  maps.put("jgg", 18);
  Set<Map.Entry<String,Integer> >  entrySet=maps.entrySet();
  for(Map.Entry<String,Integer> entry :entrySet){
   String key=entry.getKey();
   Integer value=entry.getValue();
   System.out.println(key+":"+value);
  }
在JSP页面中也经常要对Set或Map集合进行迭代
<c:forEach items="${map}"var="entry">
   ${entry.key}:${entry.value}
</c:forEach>


自定义泛型方法及其应用
1 交换数组中的两个元素的位置的泛型方法
  private static <T> void  swap(T[] a,int i,int j ){
  T tmp=a[j];
  a[j]=a[i];
  a[i]=tmp;
  //System.out.println(a[i]+"、"+a[j]);}
 public static void main(String[] args){
  String [] stt={"as","bb","cc"};
  swap(stt,0,1);          }
2 用于放置泛型的类型参数的尖括号应该出现在方法其他修饰符之后,返回值之前,就是紧邻返回值之前,按惯例类型参数通常用单个大写字母表示。
3 只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3,5)语法不能通过编译
4  除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如:Class.getAnnotation()方法的定义。并且可以用&来指定多个边界,如:
                       <V extends Serializable&cloneable> void method(){}
5 普通方法,构造方法和静态方法中都可以使用泛型。编译器也不允许创建类型变量的数组。
6 也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但不能用于catch语句
7 在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分
 例如:public static <k,v> V getValue(K key){return map.get(key);}

练习题:
1 编写一个泛型方法,自动将Object类型的对象转换为其他类型
 private static  <T> T autoObj(Object obj){
  T t=(T)obj;
  return t;
      调用:Object obj="abc";
           String st1=autoObj(obj);
2 定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象
 private static  <T> void fillArray(T [] array,T obj){
  for(T arr:array ){
   obj=arr; 
  System.out.println(obj); }
 }
3 采用自定义泛型方法的方式打印出任意参数化类型的集合中的所有内容
  这种情况下,前面的通配符方案要比泛型方法更有效,当一个类型变量用来表达两个参数之间或参数和

返回值之间的关系时,即同一个类型变量的方法签名的两处没使用,或者类型变量在方法体代码中也被使

用而不是仅在签名的时候使用,才需要使用泛型方法。
      public static <T> void printElem(Collection<T> coll){
      for(Object co:coll){
       System.out.println(co);}  
      }
   调用:ArrayList<String> arra=new ArrayList<String>();
  arra.add("p");
  arra.add("o");
  arra.add("y");
  printElem(arra);
4 定义一个方法把任意参数类型的集合中的数据安全的复制到相应类型的数组中
 public static <T> void copyColl(Collection<T> dest,T[] arrs){
  Iterator<T> it=dest.iterator();
  for(int i=0;i<dest.size();i++){
        if(it.hasNext()){
         arrs[i]=it.next();
        } 
       }
  for(T arr:arrs){
   System.out.print(arr); }
 }
调用:  ArrayList<String> arra=new ArrayList<String>();
  arra.add("p");
  arra.add("o");
  arra.add("y");
  String[] arrs=new String[3];
  copyColl(arra,arrs);

5 定义一个方法,把任意参数类型的一个数组中的数据安全的复制到相应类型的一个数组中
  public static <T> void copyArray(T [] dest,T[] arrs){
  for(int i=0;i<dest.length;i++){
   arrs[i]=dest[i];
  }
  for(T arr:arrs){
   System.out.print(arr); }
 }
调用:  Integer [] inte=new Integer[]{4,6,7,8,9,0};
  Integer [] inte2=new Integer[6];
  copyArray(inte,inte2);
类型参数的类型推断:
  copyArray(new Date[7],new String[7]);           //取交集
  copyColl(new Vector<String>(),new String[7]); 
  //copyColl(new Vector<Date>(),new String[7] );   //传播
1 编译器判断泛型方法的实际类型参数的过程称为推断,类型推断是相当于知觉推断的,其实现方法是一

个非常复杂的过程
2 根据调用泛型方法时 实际传递的参数类型或返回值类型来推断,具体规则如下:
   <1 当摸个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时

该处的实际应用类型来确定,这很容易凭感觉推断出来,即直接根据调用方法时传递的参数类型或返回值

来决定泛型参数的类型:swap(new String[3],3,4)->static <E> void swap(E[] ,int i,int j)
  <2 当某个类型变量在真个参数列表中的所有参数和返回值中多次被调用,如果调用方法时这多处的实

际应用类型都对应同一种类型来确定,这就很容易推断:add(3,4)->static <T> T add(T a,T b)
  <3 当某个类型变量在真个参数列表中的所有参数和返回值中多次被调用,如果调用方法时这多处的实

际应用类型都对应了不同类型,且没有使用返回值,这时取多个参数中的最大交集类型:
   fill(new Integer[3],3.5f)->static <T> void fill(T[]a,T v)
  <4 当某个类型变量在真个参数列表中的所有参数和返回值中多次被调用,如果调用方法时这多处的实

际应用类型都对应了不同类型,且使用返回值,这时候优先考虑返回值类型,例如下面的语句实际对应的

类型就是Integer了,编译将报告错误,将变量X的类型改为float,对比eclipse报告的错误提示,接着在

将变量X 类型改为Number,则没有了错误。int x=(3,3.5f)->static <T> T add(T a,T b)
 <5 参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为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);


定义泛型类型
 1 如果类的实例对象中有多处都要用到同一个泛型,即这些地方引用的泛型类型要保持同一个实际类型

时,这时候就采用泛型类型的方式进行定义,也就是类级别的泛型,语法:
 public class GenericDao<T>{
  private T field1;
  public void save(T obj){}
  public T getById(int id){}
}
2 类级别的泛型时根据引用该类名时 指定的类型信息来参数化类型变量的,例如:如下两种都可以
  GenericDao <String> dao=null;
  new generic Dao<String>();
3 注意:
 <1 在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型
 <2 当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静

态方法调用。因为静态成员时被所有参数化类所共享的,所以静态成员不应该有类级别的类型参数。
4 问题:类中只有一个方法使用泛型,使用类级别的还是方法级别的泛型?方法级别的。

**如何获得泛型的实际类型参数

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Vector;
public class GenericParamType {
 public static void main(String[] args)throws Exception {
  Method methodVector=GenericParamType.class.getMethod("applayVector",Vector.class);
   Type[] types=methodVector.getGenericParameterTypes();
   ParameterizedType  pType=(ParameterizedType)types[0];
   System.out.println(pType.getRawType());
   System.out.println(pType.getActualTypeArguments()[0]);
 
 }
 public static void applayVector(Vector<String> v1){ }
 }

原创粉丝点击