java泛型和注解
一、泛型是什么?
一种将设定类型的工作放到到创建对象或者调用方法的时候才去明确的特殊类型。
在使用泛型的过程中,操作的数据类型被指定为一个参数,而这种参数类型可以在类、方法
和接口中,分别被称为泛型类、泛型方法、泛型接口。(在未明确类型前,泛型的默认类型为Object类型)
二、为什么使用泛型?
1、类型安全(主要作用)
泛型的推出的主要目的是为了提高java程序的类型安全。类型安全才能保证自己的程序不会出现未定义行为。
2、消除强制类型转换
消除强制类型是泛型推出后附带的一个好处,它可以消除源代码中的许多强制类型转换,增强了代码的可读性,降低自己出错的几率。
三、Java泛型标记符
E - Element(在集合中使用,因为集合中存放的是元素)
T -Type(java类)
K -Key(键)
V -Value(值)
N -Number(数值类型)
? - 不确定的Java类型(泛型的通配符)
1、泛型类
例子:一个二维空间坐标类(命名为Space),坐标x,坐标y。
// 二维空间坐标
public class Space<T> {
// 设置的 x,y对象
private T x;
private T y;
// 无参构造
public Space() {
}
// 有参构造
public Space(T x, T y) {
this.x = x;
this.y = y;
}
// toString 方法
@Override
public String toString() {
return "Text1{" +
"x=" + x +
", y=" + y +
'}';
}
// get和set方法
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
测试所设的泛型类
// 二维空间坐标 测试类Text1
public class Text1 {
public static void main(String[] args) {
// 在<>里面设置任意的类型 如String,Integer,Double
Space<String> stringSpace = new Space<String>("北纬40°", "东经116°");
String StringX = stringSpace.getX();
String StringY = stringSpace.getY();
System.out.println("StringX:"+StringX+",StringY:"+StringY);
//这里使用的是Integer类型
Space<Integer> integerSpace = new Space<Integer>(40, 116);
Integer IntegerX = integerSpace.getX();
Integer IntegerY = integerSpace.getY();
System.out.println("IntegerX"+IntegerX+",IntegerY"+IntegerY);
//Double
Space<Double> doubleSpace = new Space<Double>(39.9, 116.3);
Double doubleSpaceX = doubleSpace.getX();
Double doubleSpaceY = doubleSpace.getY();
System.out.println("doubleSpaceX:"+doubleSpaceX+",doubleSpaceY:"+doubleSpaceY);
//测试 如果使用基本类型是否可以(以上使用的基本类型的包装类(引用类型))
// Space<int> space = new Space<int>(1,3);
// int x = space.getX();
// int y = space.getY();
// System.out.println("intx:"+x+"inty:"+y);
// 这是就会发现,运行时会报以下错误。同理,如果时使用其他的基本类型也会有相同的报错
}
}
测试结果:
int测试结果:
总结:
1、 泛型类语法为:
public clacc 类名<泛型标志,泛型标志...>{
private 泛型标志 属性名;
}
2、在指定泛型类型时,这种泛型类型必须为引用类型(基本类型的包装类),否者就会报类型转换错误。
a、通配符:
是一个实参,这是Java定义的一种特殊类型,比Object更特殊,就像一个无所不能的对象更胜于object。
class Fun1<T>{
private T a;
public void showa(){
System.out.println("a的值====="+a);
}
public T getA() {
return a;
}
public void setA(T a) {
this.a = a;
}
}
public static void main(String[] args) {
Fun1<String> stringFun1 = new Fun1<String>();
stringFun1.setA("作业快写不完了");
Fun1<Integer> integerFun1 = new Fun1<>();
integerFun1.setA(11);
Fun1<Double> doubleFun1 = new Fun1<>();
doubleFun1.setA(11.30);
// 引用传递时,必须保证类型相同
fun(stringFun1);
fun(integerFun1);
fun(doubleFun1);
}
public static void fun(Fun1<?> fun1){
// 这里使用通配符,使他可以接受任意的泛型类型 ?为泛型的通配符
fun1.showa();
}
b、受限泛型:
在引用传递中,在泛型操作中也可以设置一个泛型对象的范围上限和范围下限。
范围上限使用 extends 关键字声明,表示参数化的类型可能是所指定的类型或者是此类型的子类,而范围下限使用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,或是 Object 类
[设置上限]
声明对象: 类名称<? extends 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 extends 类>{}
[设置下限]
声明对象: 类名称<? super 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 super 类>{}
public static void main(String[] args) {
Fun1<String> stringFun1 = new Fun1<String>();
stringFun1.setA("作业快写不完了");
Fun1<Integer> integerFun1 = new Fun1<>();
integerFun1.setA(11);
Fun1<Double> doubleFun1 = new Fun1<>();
doubleFun1.setA(11.30);
fun(stringFun1); // String类型不是Number的子类
fun(integerFun1);
fun(doubleFun1);
// // 引用传递时,必须保证类型相同
// fun(stringFun1);
// fun(integerFun1);
// fun(doubleFun1);
}
// 泛型上限 :泛型类型不能超过Number 要么为Number类型要么为Number的子类类型
public static void fun(Fun1<? extends Number> fun1){
// 这里使用通配符,使他可以接受任意的泛型类型 ?为泛型的通配符
fun1.showa();
}
结果:
同理:如果使用泛型下限,也会出现相同的报错
2、泛型方法
调用方法的时候指明泛型的具体类型
【泛型方法 能够使方法独立于类的处理指定的类型】
// 定义了一个普通的方法
public <T> T fun(T t){
System.out.println("~~~~~");
return t;
}
public static void main(String[] args) {
// 调用具有fun方法的类
Text2 text2 = new Text2();
// String 类型
String string = text2.fun("String");
System.out.println(string);
// Integer 类型
Integer fun = text2.fun(1000);
System.out.println(fun);
// Double 类型
Double fun1 = text2.fun(1000.1);
System.out.println(fun1);
// Boolean 类型
Boolean fun2 = text2.fun(false);
System.out.println(fun2);
}
结果:
总结:
泛型方法的定义:
[访问权限] <泛型标识,泛型标识符....> 泛型标识(返回值类型) 方法名称(泛型标识 参数名称){ };(可以根据上面的fun方法对照查看)
3、泛型接口
表示接口中有一个未知的类型
(1)在实现泛型接口时为泛型接口指定类型
// 定义泛型接口
interface Pet<T>{
public void foot(T t);
}
/*由于上面的所设的接口类型都为T
因此我们可以直接在使用接口时需要在接口名后通过 <类型> 指定具体的类型
*/
class Cat implements Pet<String>{
@Override
public void foot(String s) {
System.out.println("小猫经常吃的食物是:"+s);
}
public class Text3 {
public static void main(String[] args) {
// 这里直接输出的String类型,是因为在接口通过后直接设置它的类型为String类型
Cat cat = new Cat();
cat.foot("猫粮");
}
}
结果:
(2) 该类也是一个泛型类,而且该类使用的泛型标志必须和接口的标志一致。
// 定义泛型接口
interface Pet1<T>{
public void age(T t);
}
// 在类实现接口时,该类也设置为泛型类,标志必须和接口的泛型标志一致
class Dog<T> implements Pet1<T>{
@Override
public void age(T t) {
System.out.println("小狗的年龄"+t);
}
}
public class Text3 {
public static void main(String[] args) {
// 这里直接输出的String类型,是因为在接口通过后直接设置它的类型为String类型
Cat cat = new Cat();
cat.foot("猫粮");
// 这里直接定义泛型的类型
Dog<Integer> objectDog = new Dog<Integer>();
objectDog.age(3);
}
}
结果:
2、注解
注解(Annontation),Java5引入的新特性,位于java.lang.annotation包中。提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。是一种说明、配置、描述性的信息,与具体业务无关,也不会影响正常的业务逻辑。但我们可以用反射机制来进行校验、赋值等操作。
常见的注解:@Override,@author,@param,@Deprecated,@SuppressWarnings
注解的用途:
- 生成文档的注解,如@author,@param。
- 跟踪代码依赖性,实现替代配置文件功能,如spring mvc的注解。
- 编译时进行格式检查,如@override。
- 编译时进行代码生成补全,如lombok插件的@Data。
注解分类:
1、预定义注解
@Override:检测被该注解标注的方法是否是继承自父类(接口)的;
@Deprecated:表示注解的内容已过时;
@FuncationInterface: 函数式接口.---要求接口钟有且仅有一个抽象方法
2、自定义注解
a、语法结构:
public @interface 注解名{
}
b、使用方法
// 自定义注解格式
@interface My{
}
@My // 类注解
class word{
@My // 属性注解
private String wordname;
@My // 方法注解 参数注解
public void worduse(@My String n){
System.out.println("n:"+n);
}
}
public class note {
public static void main(String[] args) {
word word = new word();
word.worduse("自定义注解");
}
}
结果:
3、元注解
元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。
@Retention
@Retention: 用于描述注解的生命周期,也就是该注解被保留的时间长短。@Retention 注解中的成员变量(value)用来设置保留策略,value 是 java.lang.annotation.RetentionPolicy 枚举类型。【源码---javac---字节码----运行】
SOURCE:在源文件中有效(即源文件保留)
CLASS:在 class 文件中有效(即 class 保留)
RUNTIME:在运行时有效(即运行时保留)
@Target
@Target: 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。@Target 注解有一个成员变量(value)用来设置适用目标,value 是java.lang.annotation.ElementType 枚举类型的数组
CONSTRUCTOR 用于构造方法
FIELD 用于成员变量(包括枚举常量)
LOCAL_VARIABLE 用于局部变量
METHOD 用于方法
PACKAGE 用于包
PARAMETER 用于类型参数(JDK 1.8新增)
TYPE 用于类、接口(包括注解类型)或 enum 声明
@Documented
@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。
4、自定义注解属性
@Target(value = {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD,ElementType.PARAMETER})
// 设置 自定义注解只能使用在类和方法上,默认任何位置都可以使用
@Retention(value = RetentionPolicy.RUNTIME)
//使用频繁。运行有效 Source--->Class--->Runtime 默认在字节码生效
public @interface MyNote {
String value() default ""; // 如果不写,则为默认值”“
int age() default 18;
String[] xqq() default {};
}
// 如果只为value赋值,那么可以省略value属性名。
//给定的数组值只有一个那么可以省略{},如果数组值有多个必须给定{}。
@MyNote()
//@MyNote(value = "HelloWord")
//@MyNote(xqq = {"h","e","l","l","o"})
class Text5{
private String name;
public void show(String n) {
System.out.println("n:"+n);
}
}