反射与泛型
反射
Class类
class本身是一种数据类型(Type),class/interface的数据类型是Class,JVM为每个加载的class创建唯一的Class实例。
Class实例包含该class的所有信息,通过Class实例获取class信息的方法称为反射(Reflection)
获取一个class的Class实例:
1)Class cls = String.class;
2)Class cls = "str".getClass();
3)Class cls = Class.forName("java.lang.String");
注意 Class的==比较和instanceof的区别
Integer n = new Integer(123);
boolean b3 = n instanceof Integer; // true
boolean b4 = n instanceof Number; // true
// 精确判断时
boolean b1 = n.getClass() == Integer.class; // true
boolean b2 = n.getClass() == Number.class; // false
从Class实例获取class信息:
1)getName():获取完整类名
2)getSimpleName():获取简单的类名
3)getPackage():获取包名
从Class实例判断class类型:
1)isInterface():判断是否是接口类型
2)isEnum():判断是否是枚举类型
3)isArray():判断是否是数组类型
4)isPrimitive():判断是否是基本类型
创建class实例:
cls.newInstance()
JVM总是动态加载class,可以在运行期根据条件控制加载class
// commons logging优先使用Log4j
LogFactory factory;
if(isClassPresent("org.apache.logging.log4j.Logger")){
factory = createLog4j();
}else{
factory = createJdkLog();
}
boolean isClassPresent(String name){
try{
Class.forName(name);
return true;
}catch(Exception e){
return false;
}
}
访问字段
通过Class实例获取字段field信息:
1)getField(name)——获取某个public的field(包括父类)
2)getDeclaredField(name)——获取当前类的某个field(不包括父类)
3)getFields()——获取所有public的field(包括父类)
4)getDeclaredFields()——获取当前类的所有field(不包括父类)
Field对象包含一个field的所有信息
1)getName():字段名称
2)getType():字段定义的数据类型
3)getModifiers():字段修饰符
获取和设置field的值
1)get(Object obj)
2)set(Object, Object)
通过反射访问Field需要通过SecurityManager设置的规则
通过设置setAccessible(true)来访问非public字段
调用方法
通过Class实例方法获取Method信息:
1)getMethod(name, Class ...):获取某个public的method(包括父类)
2)getDeclaredMethod(name, Class ...):获取当前类的某个method(不包括父类)
3)getMethods():获取所有public的method(包括父类)
4)getDeclaredMethods():获取当前类的所有method(不包括父类)
Method对象包含一个method的所有信息
1)getName():获取方法名
2)getReturnType():获取返回值类型
3)getParameterTypes():获取方法参数类型
4)getModifiers():获取方法修饰符
调用Method:
Object invoke(Object obj, Object ...args)
通过设置setAccessible(true)来访问非public方法
反射调用Method也遵守多态的规则
调用构造方法
Class.newInstance()只能调用public无参数构造方法
String s = (String) String.class.newInstance(); // ^_^
Integer n = (Integer) Integer.class.newInstance(); // ERROR
通过Class实例获取Constructor信息:
1)getConstructor(Class...):获取某个public 的 Constructor
2)getDeclaredConstructor(Class...):获取某个Constructor
3)getConstructors():获取所有public的Constructor
4)getDeclareConstructors():获取所有的Constructor
通过Constructor实例可以创建一个实例对象
newInstance(Object ... parameters)
通过设置setAccessible(true)来访问非public构造方法
获取继承关系
获取父类的Class:
1)Class getSuperclass()
2)Object的父类是null
3)interface的父类是null
获取当前类直接实现的interface:
1)Class[] getInterfaces()
2)getInterfaces()获取的不包括间接实现的interface;如果想获取所有的,可以递归调用获取
3)没有interface的class返回空数组
4)interface返回继承的interface
判断一个向上转型是否成立:
boolean isAssignableFrom(Class)
注解 Annotation
使用注解
注解是放在Java源码的类、方法、字段、参数前的一种标签
注解本身对代码逻辑没有任何影响,如何使用注解由工具决定
编译器可以使用的注解
@Override:让编译器检查该方法是否正确实现了覆写
@Deprecated:告诉编译器该方法已经标记为“作废”
@SuppressWarnings:让编译器忽略警告
注解可以定义配置参数和默认值
配置参数由注解类型定义
配置参数可以包括:
所有基本类型
String
枚举类型
数组
配置参数必须是常量
定义注解
使用@interface定义注解,例如
@Target(ElementType.METHOD)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
使用元注解(meta annotation)定义注解
1)@Target
2)@Retention
3)@Repeatable
4)@Inherited
@Target
使用@Target定义Annotation可以被应用于源码的哪些位置:
类或接口:ElementType.TYPE
字段:ElementType.FIELD
方法:ElementType.METHOD
构造方法:ElementType.CONSTRUCTOR
方法参数:ElementType.PARAMETER
@Retention
使用@Retention定义Annotation的生命周期
仅编译器:RetentionRolicy.SOURCE
仅class文件:RetentionRolicy.CLASS
运行期:RetentionRolicy.RUNTIME
如果@Retention不存在,则该Annotation默认为CLASS
通常自定义的Annotation都是RUNTIME
@Repeatable
使用@Repeatable定义Annotation是否可重复
JDK>=1.8
@Inherited
使用@Inherited定义子类是否可继承父类定义的Annotation
仅针对@Target为TYPE类型的Annotation
仅针对class的继承
对interface的继承无效
定义Annotation的步骤:
1)用@interface定义注解
2)用元注解配置注解
Target:必须设置
Retention:一般设置为RUNTIME
通常不必写@Inherited,@Repeatable等
3)定义注解参数和默认值
处理注解
注解本身对代码逻辑没有任何影响,SOURCE类型的注解在编译期就被丢掉了,CLASS类型的注解仅保存在class文件中,RUNTIME类型的注解在运行期可以被读取,如何使用注解由工具决定
Q:如何读取RUNTIME类型的注解?
Annotation也是class,所有Annotation继承自java.lang.annotation.Annotation,可以使用反射API
使用反射API读取Annotation:isAnnotationPresent、getAnnotation
isAnnotationPresent
判断某个注解是否存在
Class.isAnnotationPresent(Class)
Field.isAnnotationPresent(Class)
Method.isAnnotationPresent(Class)
Constructor.isAnnotationPresent(Class)
getAnnotation
获取某个注解信息
Class.getAnnotation(Class)
Field.getAnnotation(Class)
Method.getAnnotation(Class)
Constructor.getAnnotation(Class)
getParameterAnnotations
获取参数类型的注解:getParameterAnnotations()
Annotation[][] annos = m.getParameterAnnotations();
可以在运行期通过反射读取RUNTIME类型的注解
不要漏写@Retention(RetentionPolicy.RUNTIME)
可以通过工具处理注解来实现相应的功能:
1)对JavaBean的属性值按规则进行检查
2)JUnit会自动运行@Test标记的测试方法
练习
请根据注解;
@NotNull:检查该属性为非null
@Range:检查整型介于min~max,或者检查字符串长度介于min~max
@ZipCode:检查字符串是否全部由数字构成,且长度恰好为value
实现对Java Bean的属性值检查。如果检查未通过,抛出异常
泛型
什么是泛型
泛型(Generic)就是定义一种模板,例如ArrayList<T>
在代码中为用到的类创建对应的ArrayList<类型>
ArrayList<String> strList = new ArrayList<String>();
编译器会针对泛型作检查,要注意泛型的继承关系
可以吧ArrayList<Integer>向上转型为List<Integer>(T不能变)
不能把ArrayList<Integer>向上转型为ArrayList<Number>或List<Number>
ArrayList<Number>和ArrayList<Integer>两者没有继承关系
使用泛型
定义泛型类型<Number>:
List<T>的泛型接口为强类型
void add(Number)
Number get(int)
不指定泛型参数类型时,编译器会给出警告,且只能将<T>视为Object类型
编写泛型
泛型一般用在集合类中,编写类型时,需要定义泛型类型<T>
public class Pair<T> {...}
静态方法不能引用泛型类型<T>,必须定义其他类型<K>来实现泛型
public static <K> Pair<k> create(K first, K last){ ... }
泛型可以同时定义多种类型<T, K>
擦拭法
Java的泛型是采用擦拭法(Type Erasure)实现的,编译器把类型视为Object,再根据实现安全的强制转型
擦拭法是局限:
1)<T>不能是基本类型,例如int
2)Object字段无法持有基本类型
3)无法取得带泛型的Class
4)无法判断带泛型的Class
5)不能实例化T类型,因为擦拭后实际上是new Object()
可以继承自泛型类
public class IntPair extends Pair<Integer>{
}
Class<IntPair> clazz = IntPair.class;
Type t = clazz.getGenericSuperclass();
if(t instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType)t;
Type[] types = pt.getActualTypeArguments();
Type firstType = types[0];
Class<?> typeClass = (Class<?>) firstType;
System.out.println(typeClass); // Integer
}
父类类型是Pair<Integer>;子类的类型是IntPair;子类可以获取父类的泛型类型
继承关系:
extends通配符
public class Pair<T> { ... }
public class PairHelper {
static int get(Pair<Number> p) {
Number first = p.getFirst();
Number last = p.getLast();
return first.intValue() + last.intValue();
}
}
PairHelper.get(new Pair<Number>(1, 2)); // √
PairHelper.get(new Pair<Integer>(1, 2)); // ×
Pair<Integer>不是Pair<Number>的子类;get()不接受Pair<Integer>,所以可以使用<? extends Number>使方法接收所有泛型类型为Number或Number类的Pair类
public class PairHelper {
static int get(Pair<? extends Number> p) {
Number first = p.getFirst();
Number last = p.getLast();
return first.intValue() + last.intValue();
}
}
PairHelper.get(new Pair<Number>(1, 2)); // √
PairHelper.get(new Pair<Integer>(1, 2)); // √
对Pair<? extends Number>调用getFirst()方法:
方法签名:? extends Number getFirst()
可以安全赋值给Number类型的变量:Number x = p.getFirst();
不可预测实际类型就是Integer:Integer x = p.getFirst();
对Pair<? extends Number>调用setFirst()方法会报错:
方法签名:void setFirst(? extends Number)
无法传递任何Number类型给setFirst(? extends Number)
<? extends Number> 的通配符:
允许调用get方法获得Number的引用
不允许调用set方法传入Number的引用
唯一例外:可以调用setFirst(null)
<T extends Number> 的通配符
限定定义Pair<T>时只能是Number或Number的子类
super通配符
public class Pair<T> { ... }
public class PairHelper {
static int set(Pair<Integer> p, Integer first, Integer last) {
p.setFirst(first);
p.setLast(last);
}
}
PairHelper.set(new Pair<Integer>(1, 2), 3, 4); // √
PairHelper.set(new Pair<Number>(1, 2), 3, 4); // ×
Pair<Integer>不是Pair<Number>的子类;set()不接受Pair<Number>,所以可以使用<? super Integer>使方法接收所有泛型类型为Integer或Integer超类的Pair类
public class PairHelper {
static int set(Pair<? super Integer> p, Integer first, Integer last) {
p.setFirst(first);
p.setLast(last);
}
}
PairHelper.set(new Pair<Integer>(1, 2), 3, 4); // √
PairHelper.set(new Pair<Number>(1, 2), 3, 4); // √
对Pair<? super Integer>调用setFirst()方法:
方法签名:void setFirst(? super Integer)
可以安全传入Integer类型的变量:p.setFirst(new Integer(123));
对Pair<? super Integer>调用getFirst()方法会报错:
方法签名:? super Integer getFirst()
无法赋值给Integer类型的变量
<T super Integer> 的通配符
允许调用set方法传入Integer的引用
不允许调用get方法获得Integer的引用
唯一例外:可以获取Object引用Object o = p.getFirst();
<T extends Number> 的通配符
限定定义Pair时只能是Integer或Integer的超类
extends和super通配符的区别
<? extends T> 允许调用方法获取T的引用
<? super T> 允许调用方法传入T的引用
public class Collections{
// 把src的每个元素复制到dest中
public static <T> void copy(List<? super T> dest, List<? extends T> src){
for(var i=0; i<src.size(); i++){
T t = src.get(i);
dest.add(t);
}
}
}
无限定通配符<?>
不允许调用set方法(null除外)
只能调用get方法获取Object引用
可以用<T>消除<?>
泛型与反射
部分反射API是泛型
Class<T>
Constructor<T>
可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型,例如
Pair<String>[] ps = null; // √
Pair<String>[] ps = new Pair<String>[2]; // ×
Pair<String>[] ps = (Pair<String>[])new Pair[2]; // √
// 不安全地使用带泛型的数组==>通过引用
Pair[] arr = new Pair[2];
Pair<String>[] ps = (Pair<String>[]) arr;
ps[0] = new Pair<String>("a", "b");
arr[1] = new Pair<Integer>(1, 2);
// ClassCastException
Pair<String> p = ps[1];
String s = p.getFirst();
// 安全地使用带泛型的数组 ==> 不要使用arr的引用
@SuppressWarnings("unchecked")
Pair<String>[] ps = (Pair<String>[]) new Pair[2];
不能直接创建 T[] 数组:
因为擦拭后变为new Object[5];必须借助Class<T>
可以通过Array.newInstance(Class<T>, int)创建T[]数组,需要强制转型
T[] createArray(Class<T> cls){
return (T[]) Array.newInstance(cls, 5);
}
public class ArrayHelper{
@SafeVarargs // 消除编译器警告
static <T> T[] asArray(T... objs){
return objs;
}
}
String[] ss = ArrayHelper.asArray("a", "b", "c");
Integer[] ns = ArrayHelper.asArray(1, 2, 3);