通常我们在开发的时候会遇到各种相似的问题,这种问题如果不用一种方法解决的话,会造成代码冗余。
例如,我们在审批的时候,有些简单的流程只需要改变状态,我们可以有以下方法解决:
一、泛型类
public class UpdateEntityUtils<T,V extends IService<T>>{ public T setStatus(V v,String id,String status){ T t = v.getById(id); Class clazz = t.getClass(); try{ Method method = clazz.getDeclaredMethod("setStatus",String.class); method.setAccessible(true); method.invoke(t,status); return t; } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }finally { return t; } } }
这种在类名后面加上泛型的就是泛型类,实例化该类时需要传入对应的类型。
二、泛型方法
public class UpdateEntityUtils{ public <T,V extends IService<T>> T setStatus(V v,String id,String status){ T t = v.getById(id); Class clazz = t.getClass(); try{ Method method = clazz.getDeclaredMethod("setStatus",String.class); method.setAccessible(true); method.invoke(t,status); return t; } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }finally { return t; } } }
这种在方法的返回值前面加上泛型的就是泛型方法,而泛型方法可以传入任何满足的类型。
三、泛型类和泛型方法的使用区别
1、IService
public interface IService<T> { /** * 根据ID返回实体 * @param id * @return */ T getById(String id); }
2、StudentServiceImpl
public class StudentServiceImpl implements IService<Student> { @Override public Student getById(String id) { Student student = new Student(); student.setStudentName("student"); student.setStudentNo("666"); student.setStatus("studying"); return student; } }
3、TeacherServiceImpl
public class TeacherServiceImpl implements IService<Teacher> { @Override public Teacher getById(String id) { Teacher teacher = new Teacher(); teacher.setTeacherName("teacher"); teacher.setTeacherNo("888"); teacher.setStatus("teaching"); return teacher; } }
Main(泛型类)
public class Main { public static void main(String[] args) { StudentServiceImpl studentService = new StudentServiceImpl(); TeacherServiceImpl teacherService = new TeacherServiceImpl(); UpdateEntityUtils<Student,StudentServiceImpl> studentUtils = new UpdateEntityUtils<>(); studentUtils.setStatus(studentService,"666","playing"); UpdateEntityUtils<Teacher,TeacherServiceImpl> teacherUtils = new UpdateEntityUtils<>(); teacherUtils.setStatus(teacherService,"888","playing"); } }
如果使用泛型类的的话,里面的方法(非泛型方法)对只能对特定的类使用。
如果要改变不同实体类的状态的话,需要实例话多次。
Main(泛型方法)
public class Main { public static void main(String[] args) { StudentServiceImpl studentService = new StudentServiceImpl(); TeacherServiceImpl teacherService = new TeacherServiceImpl(); UpdateEntityUtils updateEntityUtils = new UpdateEntityUtils(); updateEntityUtils.setStatus(studentService,"666","playing"); updateEntityUtils.setStatus(teacherService,"888","playing"); } }
如果使用泛型方法的话,类只需要实例化一次即可,可以通过泛型方法改变不同实体类的状态。
四、泛型类和泛型方法总结
泛型类更偏向于对类其中的方法(非泛型方法)做出一种限制,泛型方法则没得这种限制,泛型类中也可以有泛型方法。
类名后面加上泛型的就是泛型类,方法的返回值前面加上泛型的就是泛型方法。
五、泛型通配符
我们在定义泛型类,泛型方法时会碰见很多不同的通配符,如 T,E,K,V 等等。
本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。
比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行。
泛型通配符可分为两类,上界通配符和下界通配符:
上界通配符:用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
下界通配符:用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型。
六、?和T的区别
比如上面的例子,我们使用了 <T,V extends IService<T>>,然后通过V调用了getById方法。
而如果我们使用<T,? extends IService<T>>,实际上这是行不通的,因为不能 ? 一个变量出来。
T表示一种确定的类型,而?表示不在意该类型是什么,也不会用到它。
7、Class<?>和Class<T>的区别
如果不是在抽象类里面,Class<T> tClass会报错,但是Class<?> aClass 不会。
因为T代表一种类型,而?可以代表任何类型,另外Class是一个泛型类,但是他的构造方法都是私有的。
八、反射
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性。
九、注解概念
我们在日常开发中,经常会用到各种注解,可能我们已经习以为常了,但是你是否注意到它是如何生效的呢。
Ⅰ、注解的格式
public @interface AnnotationName { //属性列表 public String name(); public String code(); public String msg(); }
Ⅱ、注解能使用的类型
①、八大基本数据类型(boolean,byte,char,short,int,long,float,double)
②、String
③、String和八大基本类型的一维数组
④、Class
⑤、枚举
⑥、其他的注解
Ⅲ、注解的分类
注解大致可分为三类:自定义注解、JDK内置注解、第三方框架提供的注解。
要想注解起作用必然有三步曲:定义注解,使用注解,然后读取注解。
通常我们在使用第三方框架提供的注解时,往往只能看到前两步,却不知道它在哪儿读取注解。
每个注解都像是一个标签,贴在类、方法上或者字段上,以表示它的特征。
十、总结
许多优秀的框架都是泛型、反射和注解一起使用的,它们封装好我们就可以使用了。
他们编写的注解解析器我们通常看不到,但是一定是存在的,因为看不到,所以我们用起来会感觉很美观。
他们和我们编写的注解解析器其实差不多,只是我们编写的代码少,可以很快追溯到根源。
反正我们记住要使注解起作用,必然存在注解解析器。