JAVAEE高级特性之泛型和注解技术
1. 基本注解
@Override:指定方法重写,强制一个子类必须重写父类的方法。只能作用于方法,不能作用于其他元素。
@Override public String toString() { // TODO Auto-generated method stub return super.toString(); } |
@Deprecated:用于标记某个程序元素已过时,当其他程序使用已过时的类、方法时,编译器将会给出警告
String string=new String(new byte[1],10); |
@SuppressWarnings:抑制编译器警告。(译:给编译器发出一条指令,告诉它被批注的代码内部某些警告保持静默)
@SuppressWarnings("deprecation") String string=new String(new byte[1],10); |
二.元注解
@Target(常用) |
用于指定被修饰的Annotation可以用于什么地方。 |
@Retention(常用) |
表示需要在什么级别保存该注解信息。 |
@Documented |
将此注解包含在JavaDoc中 |
@Inherited |
允许子类继承父类中的注解 |
@Target,@Retention
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String name() default "admin"; int age() default 10; } |
@Target可用的ElementType参数包括
ANNOTATION_TYPE |
指定该策略的Annotation只能修饰Annotation |
CONSTRUCTOR |
指定该策略的Annotation只能修饰构造器 |
FIELD |
指定该策略的Annotation只能修饰成员变量 |
LOCAL_VARIABLE |
指定该策略的Annotation只能修饰局部变量 |
METHOD |
指定该策略的Annotation只能修饰方法定义 |
PACKAGE |
指定该策略的Annotation只能修饰包定义 |
PARAMETER |
指定该策略的Annotation只能修饰参数 |
TYPE |
指定该策略的Annotation可以修饰类、接口(包括注解类型)或枚举定义 |
@Retention可选的RetentionPolicy参数包括:
SOURCE |
编译器直接丢弃这种策略的注解 |
CLASS |
编译器将把注解记录在class文件中。当运行Java程序时,JVM不再保留注解。这是默认值 |
RUNTIME |
编译器将把注解记录在class文件中。当运行Java程序时,JVM仍会保留注解,程序可以通过反射获取该注解 |
自定义注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String name() default "admin"; int age() default 10; } |
提取注解信息
AnnotatedElement接口是所有程序元素(如Class、Method)的父接口,程序通过反射获取了某个类的AnnotatedElement对象之后,就可以通过该对象的方法来访问Annotation信息。
Annotation getAnnotation(Class<T> annotationClass) |
返回该程序元素上存在的、指定类型的注解,如果该类型的注解不存在,则返回null |
Annotation[] getAnnotations( ) |
返回该程序元素上存在的所有注解 |
boolean isAnnotationPressent(Class<? extend Annotation> annotationClass) |
判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false |
第一种:
Class clazz=Person.class; Annotation[] annotations = clazz.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } |
第二种:如果我们需要获取某个注解里的元数据,则可以将注解类型强制转换成所需的注解类型,然后通过注解对象的抽象方法来访问这些元数据。
Annotation annotation = clazz.getAnnotation(MyAnnotation.class); int age = ((MyAnnotation)annotation).age(); String name2 = ((MyAnnotation)annotation).name(); System.out.println(name2+"------"+age); |
三.泛型
Java的集合有个缺点:当我们把一个对象“丢进”集合里后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类型。
为了解决这个问题,从JDK1.5以后,Java引入了“参数化类型(parametrized type)”的概念,允许我们在创建集合时指定集合元素的类型。如List<String>,这表明该List集合只能保存字符串类型的对象。Java的参数化类型被称为泛型(Generic)。
泛型允许在定义接口、类时指定类型形参,类型形参在整个接口、类中可当成类型使用。
定义泛型接口、类
当我们使用List类型时,为形参E传入String类型实参,则产生了一个新的类型:List<String>类型,我们可以把List<String>想象成E被替换成String的特殊List子接口:
程序虽然定义了一个List<E>接口,但实际使用时可以产生无数多个List接口,只要为E传入不同的类型参数,系统就会多出一个新的List子接口
注意:“ListString”只是逻辑上存在,物理上并不存在。
当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类派生子类,但是当使用这些接口、父类时不能再包含类型参数。例如下面的代码就是错误的:
这就好比我们定义方法的时候可以使用形参,但调用方法时必须传入实参。所以我们需要把上面代码的T换成实际类型。上面的代码应改为:
深入泛型
当我们使用一个泛型类时,应该为这个泛型类传入一个类型实参,如果没有传入实参,就会引起泛型警告
public void test(List c) { for(int i = 0;i<c.size();i++){ System.out.println(c.get(i)); } } |
类型通配符
为了表示各种泛型List的父类,我们需要使用类型通配符,类型通配符是一个问号“?”,将一个问号作为类型实参传给List集合,写作:List<?>(意思是未知类型元素的List)。这个?被称为通配符,它可以匹配任何类型。
public void test(List<?> c) { for(int i = 0;i<c.size();i++){ System.out.println(c.get(i)); } } |
List<?>是任何泛型List的父类型
类型通配符上限
public class Shape { public void draw(Canvas c){ System.out.println("画方"); } } public class Circle extends Shape{ public void draw(Canvas c){ System.out.println("画圆"); } } public class Canvas{ public void drawAll(List<Shape> shapes){ for(Shape s:shapes){ s.draw(this); } } public static void main(String[] args) { List<Circle> circleList = new ArrayList<Circle>(); circleList.add(new Circle()); Canvas c = new Canvas(); c.drawAll(circleList); } } |
为了解决上述问题,可以修改代码如下:
public class Canvas{ public void drawAll(List<?> shapes){ for(Shape s:shapes){ s.draw(this); } } public static void main(String[] args) { List<Circle> circleList = new ArrayList<Circle>(); circleList.add(new Circle()); Canvas c = new Canvas(); c.drawAll(circleList); } } |
通配符可以保证编译通过,但带来了类型安全问题
实际上,我们需要一种泛型表示方法,它可以表示所有Shape泛型List的父类而不是所有泛型List的父类。为了满足这种需求,Java泛型提供了被限制的通配符
语法:
public class Canvas{ public void drawAll(List<? extends shapes> shapes){ for(Shape s:shapes){ s.draw(this); } } public static void main(String[] args) { List<Circle> circleList = new ArrayList<Circle>(); circleList.add(new Circle()); Canvas c = new Canvas(); c.drawAll(circleList); } } |
泛型方法
JDK1.5还提供了泛型方法的支持。
示例:将一个Object数组的所有元素添加到一个Collection集合中。
static void fromArrayToCollection(Object[] objs,Collection<Object> c){ for(Object o:objs){ c.add(o); } } public static void main(String[] args) { List<Integer> c = new ArrayList<Integer>(); Integer[] objs = new Integer[2]; fromObjectToCollection(objs,c);//报错 } |
类型通配符在这里是不适用的,因为无法将对象添加到不知道具体类型的集合中
语法:
修改前面代码:
与接口、类定义的类型形参相比,泛型方法定义的类型形参只能在方法内部使用
前面代码编译器根据实参可以推断出最直接的类型形参的类型。为了让编译器能够准确的推断出泛型方法中类型形参的类型,不要制造迷惑!
static <T> void test(Collection<T> a,Collection<T> b){ for(T o:a){ b.add(o); } } public static void main(String[] args) { List<Object> objList = new ArrayList<Object>(); List<String> strList = new ArrayList<String>(); //这里将发生编译错误 test(strList,objList); } |
static <T> void test(Collection<? extends T> a,Collection<T> b){ for(T o:a){ b.add(o); } } public static void main(String[] args) { List<Object> objList = new ArrayList<Object>(); List<String> strList = new ArrayList<String>(); test(strList,objList); } |
前面所使用的test方法可以将集合a中的元素复制到集合b,假设该方法还需要一个返回值,返回最后一个被复制的元素,修改该方法如下:
static <T> T test(Collection<? extends T> a,Collection<T> b){ T last = null; for(T o:a){ last = o; b.add(o); } return last; } public static void main(String[] args) { List<Number> nl = new ArrayList<Number>(); List<Integer> il = new ArrayList<Integer>(); //下面代码将引起编译错误 Integer last = test(il,nl); } |
如果要保留a集合的类型,需要使用类型通配符下限,修改该方法如下:
static <T> T test(Collection<? extends T> a,Collection<? super T> b){ T last = null; for(T o:a){ last = o; b.add(o); } return last; } public static void main(String[] args) { List<Number> nl = new ArrayList<Number>(); List<Integer> il = new ArrayList<Integer>(); Integer last = test(il,nl); } |
? extends xxx 只能用于方法传参,因为jdk能够确定传入为xxx的子类,返回只能用Object类接收
? supper xxx 只能用于方法返回,jdk能够确定此类的最小继承边界为xxx,只要是这个类的父类都能接收,
但是传入参数无法确定具体类型,只能接受null的传入。
? 既不能用于方法参数传入,也不能用于方法返回
- 点赞
- 收藏
- 关注作者
评论(0)