JAVAEE高级特性之泛型和注解技术

举报
tea_year 发表于 2022/02/28 20:50:28 2022/02/28
【摘要】 1.​ 基本注解​ @Override:指定方法重写,强制一个子类必须重写父类的方法。只能作用于方法,不能作用于其他元素。 @Override public String toString() { // TODO Auto-generated method stub return super.toString(); }​ @Deprecated:用于标记某个程序元素已过时,当其他程序使用...

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的传入。


? 既不能用于方法参数传入,也不能用于方法返回

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。