1,什么是泛型
2,为什么使用泛型
3,如何定义泛型
4,通配符和泛型的限制
5,如果定义泛型接口
6,如果定义泛型方法
7,注解
7.1预定义注解
7.2自定义注解
7.3元注解
1,什么是泛型
泛型(Generics)是一种在编译时期提供类型安全性的机制。它允许在定义类、接口或方法时使用参数化类型,这样就可以在使用时指定具体的类型。
使用泛型的好处是可以编写通用的、可重用的代码,同时保证类型的安全性。通过泛型,我们可以在编译时期检查类型,避免在运行时出现类型转换错误。
泛型可以应用于类、接口、方法等,使用一对尖括号(<>)来指定泛型类型。
2,为什么使用泛型
比如在使用Object
类型时,需要进行强制类型转换。如果在进行类型转换时出现错误,可能会在运行时抛出ClassCastException
异常。这种类型转换错误只能在运行时被检测到,因此存在一定的安全隐患。
而使用泛型(Generics),可以在编译时期就对类型进行检查,提供了更强的类型安全性。使用泛型,我们可以在定义类、接口或方法时指定参数化类型,从而在使用时避免类型转换错误
如下图所示出现的类型转化异常(ClassCastException):
@Data @AllArgsConstructor @NoArgsConstructor public 代码class Point { *//**创建一个坐标位置类 \* private Object x; private Object y; }
package com.gll.date0809.test.t1; public class Test01 { public static void main(String[] args) { //x和y的值都是整型 Point point = new Point(130, 88); //x和y都是字符串类型 Point point1 = new Point("东经35", "北纬138"); //不同类型代码 Point point2 = new Point("东经66", 255); String x = (String) point2.getX(); String y = (String) point2.getY(); //当运行时会报错classCastException } }
3,如何定义泛型
语法:
public class 类名<泛型标志,泛型标志......>{ private 泛型标志 属性名; //泛型可是是任何字符,习惯用T表示 }
@Data @NoArgsConstructor @AllArgsConstructor public class Point2<T> { private T x; private T y; } class test02{ public static void main(String[] args) { Point2<String> stringPoint2 = new Point2<>("东经101", "北纬35"); String x = stringPoint2.getX(); String y = stringPoint2.getY(); //编译时类型 List<String> list = new ArrayList<>(); list.add("Hello"); list.add(123); // 编译时报错 String str = list.get(1);//编译时类型检查,不再需要类型转换 } }
注意:指定泛型类型时,必须为引用类型。
4,通配符
在开发中对象的引用传递是最常见的,但是如果在泛型的操作中,在进行引用传递时泛型类型必须匹配才可以传递,否则无法进行传递。
@Data @NoArgsConstructor @AllArgsConstructor public class Info3<T> { private T var; public void show(){ System.out.println("今天立秋,天气很好,"+var); } } class Test3{ public static void main(String[] args) { Info3<String> stringInfo3 = new Info3<>(); stringInfo3.setVar("继续加油吧!"); fun(stringInfo3); } public static void fun(Info3<?> info){//能否该方法让它接收任意的泛型类型,则必须使用通配符,?是泛型的通配符 info.show(); } }
4.2受限泛型
在引用传递中,在泛型操作中也可以设置一个泛型对象的范围上限和下限。范围上限使用extends关键字声明,表示参数化的类型可能是所指定的类型或者是此类型的子类,而范围下限使用super进行声明,表示参数化类型可能是所指定的类型或者此类型的父类型。
public class Test4 { public static void main(String[] args) { Info4<String> stringInfo4 = new Info4<>(); Info4<Integer> integerInfo4 = new Info4<>(); Info4<Object> objectInfo4 = new Info4<>(); Info4<Number> numberInfo4 = new Info4<>(); fun1(stringInfo4);//String 报错,String不是Number的子类 fun1(integerInfo4); fun1(numberInfo4); fun2(integerInfo4);//integer 报错,integer 不是Number的父类 fun2(objectInfo4); } //泛型的上限,--泛型的类型不能超过Number,要么是Number,要么是Number的子类类型; public static void fun1(Info4<? extends Number> info4){ info4.show(); } //泛型的下限,--泛型的类型不能小于Number,要么是Number,要是是Number的父类(Object); public static void fun2(Info4<? super Number> info4){ info4.show(); } } @Data @NoArgsConstructor @AllArgsConstructor class Info4<T> { private T var; public void show(){ System.out.println("今天立秋,天气很好,"+var); } }
5,泛型接口
语法:
public interface 接口名 <泛型标识,......>{ }
接口用来被类实现,一个类如何实现泛型接口,有两种方式:
(1)在实现泛型接口时为泛型接口指定类型
(2)该类也是泛型类,而且该类所使用的泛型标识必须和该接口的泛型标识一样
public class Test5 { public static void main(String[] args) { Mouse mouse = new Mouse(); mouse.show("来自郑州"); KeyBoard<Integer> integerKeyBoard = new KeyBoard<>(); integerKeyBoard.show(88); } }
public interface USB <T>{ public abstract void show(T t); } //第一种使用泛型接口时为其指定类型 class Mouse implements USB<String>{ @Override public void show(String s) { System.out.println("我使用的是罗技鼠标"+s); } } //第二种类实现接口时,类也定义为泛型类,且该类泛型标准必须和接口泛型标志一致 class KeyBoard<T> implements USB<T>{ @Override public void show(T t) { System.out.println("我使用的是惠普键盘"+t); } }
6,泛型方法
前面学习的所有泛型操作都是将整个类进行泛型化,但同样可以在类中定义泛型化的方法。泛型方法的定义与其所在的类是否是泛型类“没有任何的关系”,所在的类可以说泛型类,也可以不是泛型类。
泛型方法语法:
[访问权限] <泛型标识> 返回值泛型标识 方法名称(泛型标识 参数名称){
}
public class Test6 { public static void main(String[] args) { Test6 test6 = new Test6(); test6.show(666); } //泛型方法 public <T> void show(T t){ System.out.println("天黑了,"+t); } }
7.注解
Java注解(Annotation)是一种元数据(metadata)的形式,它可以在Java代码中添加额外的信息、标记或说明。注解可以用于类、方法、字段、参数等元素上,用于提供给编译器、工具和运行时环境使用。
Java注解的语法使用@
符号后跟注解的名称,放置在目标元素的前面。
7.1预定义注解
@Override: 当方法使用该注解时,表示该方法是一个重写的方法,那么该方法必须符合重写的规则
【子类重写的方法必须和父类的方法名一致,参数一致,返回值也要一致,访问修饰符不能小于父类的<public protected 默认 private> 抛出的异常不能大于父类】。
@Deprecated:标记该方法已经过时。jvm
@FuncationInterface: 函数式接口, --要求接口有且仅有一个抽象方法。
7.2自定义注解
语法:
public @interface 注解名{ }
使用方法:
@注解名
@注解名 public void show(){ }
public class Test7 { public static void main(String[] args) { Day day = new Day(); day.rain("下雨"); } } //自定义注解 @interface MyAnnotation{ } @MyAnnotation class Day{ @MyAnnotation private String weather; @MyAnnotation public void rain(String weather){ System.out.println("今天,"+weather); } }
使用了自定义注解并没有任何意思,要想让它有意义,必须结合反射来解析这些注解,并赋予相应的功能。
@Override: --jvm解析了该注解。
@Controller: --spring框架解析了该注解。
7.3元注解
思考:@controller只能在类上,@GetMapping 只能使用在方法。为什么这些注解可以限制它的使用范围。因为它们用元注解来限制。
@Retention:限制注解什么时候生效。【源码--javac--字节码--运行】
//使用在类上 TYPE, //使用在属性上 FIELD, //使用在方法上 METHOD, //使用在参数 PARAMETER, //使用在构造方法上 CONSTRUCTOR,
@Documented: 在生成api文档是否含有该注解。
7.4自定义注解
语法:
public @interface 注解名{ 数据类型 属性名() default 默认值; //如果没有设置默认值,那么在使用盖饭注解必须为该属性指定值。 } 数据类型可以用哪些类型: 基本类型【8个】,及基本类型的数组。
public class Test7 { public static void main(String[] args) { Day day = new Day(); day.rain("xiayu"); } } @Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) //使用最多运行时有效,【Source--Class--Runtime】 ,默认值是在字节码生效 @Documented @interface MyAnnotation{ String value() default ""; int age() default 18; String[] hobby() default {}; } //如果是value赋值,那么可以省略属性名 @MyAnnotation(value="hello")//给定的数值只有一个可以省略,多个必须给定属性名 class Day{ @MyAnnotation private String weather; @MyAnnotation public void rain(String weather){ System.out.println("jintian,"+weather); } }
完。