• 初识Java 17-2 反射


    目录

    转型前检查

    构建例子:生成层次结构

    优化Creator:使用类字面量

    优化PetCounter:动态验证类型

    更通用的递归计数

    注册工厂


    本笔记参考自: 《On Java 中文版》


    转型前检查

            当我们使用传统的类型转换,例如:

    1. Object x = new Shape();
    2. Shape s = (Shape)x; // 传统的类型转换

    此时Java就会执行反射,这种转型通常被称为“类型安全向下转型”。不同于向上转型,编译器不会知道这个Object实际的类型是什么。因此在向下转型时,我们需要声明对象的确切类型

        C++在这种强制类型转换时不会进行反射,而是进行“运行时类型识别”。告诉编译器,这是一个新的类型。

            而除了这种类型转换和Class对象外,Java还提供了其他反射的方式:instanceof关键字。这一关键字可以用于判断对象是否是特定类型的实例,例如:

    【例子:instanceof关键字的使用】

    1. public class ClassInstanceof {
    2. public static void main(String[] args) {
    3. Object obj = new Circle();
    4. if (obj instanceof Shape)
    5. System.out.println("这个实例是一个Shape");
    6. if (obj instanceof Circle)
    7. System.out.println("这个实例是一个Circle");
    8. if (obj instanceof Square)
    9. System.out.println("这个实例是一个Square");
    10. else
    11. System.out.println("这个实例的类型不是Square");
    12. }
    13. }

            程序执行的结果是:

            instanceof的存在使得程序员可以使用Java轻松识别出对象的正确类型。

    ---

    构建例子:生成层次结构

            为了方便后续的示例,这里先创建一个比较复杂的层次结构(人和宠物):

            在这一章的笔记中,只会使用到Pet类及其子类。但为了结构的完整性,这里展示的是整个继承结构。Individual作为所有类的基类,会被用到的部分只有其重写的toString()

    1. @Override
    2. public String toString() {
    3. return getClass().getSimpleName() +
    4. (name == null ? "" : " " + name);
    5. }

            Pet及其子类的实现都较为简单,这里只选取其中一条分支进行展示(其他均与该分支相同):

            这些类都有一个无参构造器,这样我们就可以调用newInstacnce()来生成实例。下面的例子是一个用于随机生成Pet对象的工具类:

    【例子:随机生成Pet对象】

    1. package reflection.pets;
    2. import java.lang.reflect.InvocationTargetException;
    3. import java.util.ArrayList;
    4. import java.util.List;
    5. import java.util.Random;
    6. import java.util.function.Supplier;
    7. import java.util.stream.Collectors;
    8. import java.util.stream.Stream;
    9. public abstract class Creator
    10. implements Supplier {
    11. private Random random = new Random(47);
    12. // types()方法用于创建不同的Pet对象,
    13. // 我们要求这个方法必须在子类中进行实现
    14. // (一般,这个方法只需要返回一个静态的List引用即可)
    15. public abstract Listextends Pet>> types();
    16. // 创建一个随机的Pet对象:
    17. @Override
    18. public Pet get() {
    19. int n = random.nextInt(types().size());
    20. try {
    21. return types().get(n)
    22. .getConstructor().newInstance();
    23. // 调用newInstance(),会得到四个异常:
    24. } catch (InstantiationException |
    25. NoSuchMethodException |
    26. InvocationTargetException |
    27. IllegalAccessException e) {
    28. throw new RuntimeException(e);
    29. }
    30. }
    31. public Stream stream() {
    32. return Stream.generate(this);
    33. }
    34. public Pet[] array(int size) { // 调用上面的stream()方法
    35. return stream().limit(size).toArray(Pet[]::new);
    36. }
    37. public List list(int size) {
    38. return stream().limit(size) // 将元素收集到一个ArrayList中
    39. .collect(Collectors.toCollection(ArrayList::new));
    40. }
    41. }

            Creator是一个生成器的基类,它的types()是一个抽象方法,这样我们就可以要求Creator的基类去实现这个方法。这是一个模板方法模式的例子。值得一提的是,types()的返回类型被规定成一个包含Class对象的List,这个List可以接受Pet及其的任意子类

            接下来尝试为Creator实现一个子类:

    【例子:使用forName()的生成器】

    1. package reflection.pets;
    2. import java.util.ArrayList;
    3. import java.util.List;
    4. public class ForNamePetCreator extends Creator {
    5. // 应该确保:在类被初始化时,就向manyTypes列表中加载数据
    6. // (也就是说,在类被加载时就应该调用loader()方法)
    7. private static Listextends Pet>> manyTypes =
    8. new ArrayList<>();
    9. // 设置我们需要生成的类型:
    10. private static String[] typeNames = {
    11. // Dog的子类:
    12. "reflection.pets.Mutt",
    13. "reflection.pets.Pug",
    14. // Cat的子类:
    15. "reflection.pets.Chero",
    16. "reflection.pets.Manx",
    17. "reflection.pets.Cymric",
    18. // Rodent的子类:
    19. "reflection.pets.Rat",
    20. "reflection.pets.Mouse",
    21. "reflection.pets.Hamster"
    22. };
    23. @SuppressWarnings("unchecked")
    24. private static void loader() {
    25. try {
    26. for (String name : typeNames)
    27. manyTypes.add(
    28. (Classextends Pet>) Class.forName(name));
    29. } catch (ClassNotFoundException e) {
    30. throw new RuntimeException(e);
    31. }
    32. }
    33. static {
    34. loader();
    35. }
    36. // types()方法只需要用于返回静态的List引用即可
    37. @Override
    38. public Listextends Pet>> types() {
    39. return manyTypes;
    40. }
    41. }

            通过Class.forName(),我们就设置好的类的完全限定名全部录入到manyTypes列表中。因为我们放入forName()中的是在编译时无法被验证的字符串,因此它可以会报错。

            需要提一下这里的静态初始化块:

    1. static {
    2. loader();
    3. }

    由于@SuppressWarnings()不允许被直接用于静态初始化块,因此我们需要将loader()放入一个静态初始化块中。这样当类第一次被使用时,loader()就会被唯一一次调用,保证manyTypes()被正确初始化。

            instanceof关键字可用于检测对象的类型,下面的例子会通过这一关键字完成对各种Pet类型的数量监控。因为需要反映各个类型与其数量之间的对应关系,所以我们可以使用Map

    【例子:监控各个Pet类型的数量】

    1. package reflection;
    2. import java.util.HashMap;
    3. import reflection.pets.*;
    4. // 用于跟踪各种类的Pet数量
    5. public class PetCounter {
    6. static class Counter extends HashMap {
    7. public void count(String type) {
    8. // get()返回type映射的值,若type不在Map中,返回null
    9. Integer quantity = get(type);
    10. if (quantity == null) {
    11. // 将键和值进行绑定
    12. put(type, 1);
    13. } else
    14. put(type, quantity + 1);
    15. }
    16. }
    17. private Counter counter = new Counter();
    18. private void countPet(Pet pet) {
    19. System.out.print(
    20. pet.getClass().getSimpleName() + " ");
    21. // 注意:instanceof只能与命名类型比较
    22. if (pet instanceof Pet)
    23. counter.count("Pet");
    24. // 属于Dog的:
    25. if (pet instanceof Dog)
    26. counter.count("Dog");
    27. if (pet instanceof Mutt)
    28. counter.count("Mutt");
    29. if (pet instanceof Pug)
    30. counter.count("Pug");
    31. // 属于Cat的:
    32. if (pet instanceof Cat)
    33. counter.count("Cat");
    34. if (pet instanceof Chero)
    35. counter.count("Chero");
    36. if (pet instanceof Manx)
    37. counter.count("Manx");
    38. if (pet instanceof Cymric)
    39. counter.count("Cymric");
    40. // 属于Rodent的:
    41. if (pet instanceof Rodent)
    42. counter.count("Rodent");
    43. if (pet instanceof Rat)
    44. counter.count("Rat");
    45. if (pet instanceof Mouse)
    46. counter.count("Mouse");
    47. if (pet instanceof Hamster)
    48. counter.count("Hamster");
    49. }
    50. public void count(Creator creator) {
    51. creator.stream().limit(20)
    52. .forEach(pet -> countPet(pet));
    53. System.out.println();
    54. System.out.println(counter);
    55. }
    56. public static void main(String[] args) {
    57. new PetCounter().count(new ForNamePetCreator());
    58. }
    59. }

            程序执行的结果是:

            这个例子体现了instanceof的局限性:通过这一关键字,一个对象只能与命名类型进行比较,而不能与一个Class对象进行比较。这意味着,我们不能将我们需要比对的类型放入一个Class数组中,再通过循环等方式实现instanceof的自动化。

        不过,若代码中存在很多instanceof,这个代码可能也是存在问题的。


    优化Creator:使用类字面量

            可以使用类字面量的方式优化Creator,通过这种方式实现的Creator会更加清晰:

    【例子:重新实现Creator

    1. package reflection.pets;
    2. import java.util.Arrays;
    3. import java.util.Collections;
    4. import java.util.List;
    5. // 使用字类面量重新实现Creator
    6. public class PetCreator extends Creator {
    7. // 因为使用的是字面量,所以不需要生成实例
    8. public static final
    9. Listextends Pet>> ALL_TYPES =
    10. Collections.unmodifiableList(Arrays.asList(
    11. Pet.class, Dog.class, Cat.class, Rodent.class,
    12. Mutt.class, Pug.class,
    13. Chero.class, Manx.class, Cymric.class,
    14. Rat.class, Mouse.class, Hamster.class
    15. ));
    16. // 进行类型的随机生成(限定:只生成子类)
    17. private static final
    18. Listextends Pet>> TYPES =
    19. ALL_TYPES.subList(
    20. ALL_TYPES.indexOf(Mutt.class),
    21. ALL_TYPES.size());
    22. @Override
    23. public Listextends Pet>> types() {
    24. return TYPES;
    25. }
    26. public static void main(String[] args) {
    27. System.out.println(TYPES);
    28. List pets = new PetCreator().list(7);
    29. System.out.println(pets);
    30. }
    31. }

            程序执行的结果是:

            因为使用的是字面量的缘故,不会用到newInstance()进行实例生成。也因此省去了处理异常的功夫。

            ALL_TYPES包含了所有的Pet类型,方便任何继承了这个类的子类进行使用。而types()返回的TYPES则包含了所有确切的宠物类型,因此可用于生成随机的Pet

        Collections.unmodifiableList()会根据传入的原始列表返回一个只读视图,其空间开辟在堆上。Java 9还加入了一个List.of()方法,这个方式生成的视图存在于内存中,因此速度会更快一点。

            因为之前的PetCounter.count()接受的是一个Creator参数,因此我们也可以使用它来测试这个新的PetCreator

    【例子:测试PetCreator

    1. package reflection;
    2. import reflection.pets.PetCreator;
    3. public class PetCounter2 {
    4. public static void main(String[] args) {
    5. new PetCounter().count(new PetCreator());
    6. }
    7. }

            程序执行的结果是:


    优化PetCounter:动态验证类型

            很显然,PetCounter中的instanceof过于冗长,而Class.isInstance()提供了一种更好的方式动态验证对象的类型,可以用它替代PetCounter中的instanceof

    【例子:使用isInstance()

    1. package reflection;
    2. import onjava.Pair;
    3. import reflection.pets.Pet;
    4. import reflection.pets.PetCreator;
    5. import java.util.HashMap;
    6. import java.util.stream.Collectors;
    7. public class PetCounter3 {
    8. static class Counter
    9. extends HashMapextends Pet>, Integer> {
    10. Counter() {
    11. super(PetCreator.ALL_TYPES.stream()
    12. .map(type -> Pair.make(type, 0)) // 将所有的Pet类型的计数初始化为0
    13. .collect(
    14. Collectors.toMap(Pair::key, Pair::value)));
    15. }
    16. public void count(Pet pet) {
    17. // 使用Cass.isInstance()消除instanceof
    18. entrySet().stream() // entrySet():返回由包含在这个Map视图中的元素决定的Set视图
    19. .filter(pair -> pair.getKey().isInstance(pet))
    20. .forEach(pair ->
    21. put(pair.getKey(), pair.getValue() + 1));
    22. }
    23. @Override
    24. public String toString() {
    25. String result = entrySet().stream()
    26. .map(pair -> String.format("%s=%s",
    27. pair.getKey().getSimpleName(),
    28. pair.getValue()))
    29. .collect(Collectors.joining(", "));
    30. return "{" + result + "}";
    31. }
    32. }
    33. public static void main(String[] args) {
    34. Counter petCount = new Counter();
    35. new PetCreator().stream()
    36. .limit(20)
    37. .peek(petCount::count) // 对每个流元素进行计数
    38. .forEach(p -> System.out.print(
    39. p.getClass().getSimpleName() + " "));
    40. System.out.println("\n" + petCount);
    41. }
    42. }

            程序执行的结果是:

            isInstance()的使用使得我们不再需要使用instanceof

            为了能够为所有的Pet类型计数,就需要在创建类的时候预加载所有的Pet类。因此内部类Counter就需要在其创建的时候将所有类型进行加载,这就用到了之前设置的ALL_TYPES。也因此,只需要修改ALL_TYPES就可以增删类型。


    更通用的递归计数

            上一个例子需要在使用前加载所有的Pet类型,但我们也可以使用isAssignableFrom()方法来替代这一预加载过程,在方法执行时对每一个将要计数的对象进行类型判断。这样,我们就可以获得一个更加通用的计数方法:

    【例子:对某一类型的实例进行计数】

    1. package onjava;
    2. import java.util.HashMap;
    3. import java.util.stream.Collectors;
    4. public class TypeCounter
    5. extends HashMap, Integer> {
    6. private Class baseType;
    7. public TypeCounter(Class baseType) {
    8. this.baseType = baseType;
    9. }
    10. public void count(Object obj) {
    11. Class type = obj.getClass();
    12. if (!baseType.isAssignableFrom(type))
    13. throw new RuntimeException(
    14. obj + "是一个错误的类型:" + type +
    15. ",应该传入" + baseType + "或其的子类");
    16. countClass(type);
    17. }
    18. // 先对基类进行计数。若存在父类,则递归调用自身,对父类进行计数。
    19. private void countClass(Class type) {
    20. Integer quantity = get(type);
    21. put(type, quantity == null ? 1 : quantity + 1);
    22. Class superClass = type.getSuperclass();
    23. if (superClass != null &&
    24. baseType.isAssignableFrom(superClass)) {
    25. countClass(superClass);
    26. }
    27. }
    28. @Override
    29. public String toString() {
    30. String result = entrySet().stream()
    31. .map(pair -> String.format("%s=%s",
    32. pair.getKey().getSimpleName(),
    33. pair.getValue()))
    34. .collect(Collectors.joining(", "));
    35. return "{" + result + "}";
    36. }
    37. }

            isAssignableFrom()方法用于验证传递的对象是否存在于要求的层次结构中。

            在上述的类中,countClass()方法就是用于递归计数的,它在完成子类的计数后会进行判断,若存在父类,则对父类进行计数。

            现在,可以使用上面的类了:

    【例子:使用TypeCounter

    1. package reflection;
    2. import onjava.TypeCounter;
    3. import reflection.pets.Pet;
    4. import reflection.pets.PetCreator;
    5. public class PetCounter4 {
    6. public static void main(String[] args) {
    7. TypeCounter counter = new TypeCounter(Pet.class);
    8. new PetCreator().stream()
    9. .limit(20)
    10. .peek(counter::count)
    11. .forEach(pet -> System.out.print(
    12. pet.getClass().getSimpleName() + " "));
    13. System.out.println("\n" + counter);
    14. }
    15. }

            程序执行的结果如下:

    注册工厂

            在上述例子实现Creator的时候,还存在一个问题:

        若需要为Pet添加一个新的子类,就需要在Creator的实现PetCreator等)中添加对应的类型。若我们需要较为频繁地添加子类,这就会给我们造成一定的麻烦。

            对于这种情况,一般有两种做法:

    1. 手动创建列表,并将这个列表放在较为核心的代码部分,例如基类之中。
    2. 使用工厂方法设计模式推迟对象的创建,将创建交付给类自己去完成。工厂方法可以被多态地调用,来创建恰当类型的对象。

            java.util.function.SupplierT get()方法就是一个工厂方法的原型。这一方法可以通过协变返回类型(基类方法返回值的子类型)完成对象的返回。

            下面的示例包含一个工厂对象(Supplier)的静态List

    【例子:使用工厂方法进行注册】

    1. package reflection;
    2. import java.util.Arrays;
    3. import java.util.List;
    4. import java.util.Random;
    5. import java.util.function.Supplier;
    6. import java.util.stream.Stream;
    7. class Part implements Supplier {
    8. @Override
    9. public String toString() {
    10. return getClass().getSimpleName();
    11. }
    12. static Listextends Part>> prototypes =
    13. Arrays.asList( // 将类型的工厂类添加到列表中,完成“注册”
    14. new FuelFilter(),
    15. new AirFilter(),
    16. new CabinAirFilter(),
    17. new OilFilter(),
    18. new FanBelt(),
    19. new PowerSteeringBelt(),
    20. new GeneratorBelt());
    21. private static Random rand = new Random(47);
    22. @Override
    23. public Part get() {
    24. int n = rand.nextInt(prototypes.size());
    25. // 第一个get(n)调用自Arrays
    26. // 第二个get()才是Supplier的
    27. return prototypes.get(n).get();
    28. }
    29. }
    30. // 分类器,不存在实现
    31. class Filter extends Part {
    32. }
    33. class FuelFilter extends Filter {
    34. @Override
    35. public FuelFilter get() {
    36. return new FuelFilter();
    37. }
    38. }
    39. class AirFilter extends Filter {
    40. @Override
    41. public AirFilter get() {
    42. return new AirFilter();
    43. }
    44. }
    45. class CabinAirFilter extends Filter {
    46. @Override
    47. public CabinAirFilter get() {
    48. return new CabinAirFilter();
    49. }
    50. }
    51. class OilFilter extends Filter {
    52. @Override
    53. public OilFilter get() {
    54. return new OilFilter();
    55. }
    56. }
    57. // 分类器,不存在实现
    58. class Belt extends Part {
    59. }
    60. class FanBelt extends Belt {
    61. @Override
    62. public FanBelt get() {
    63. return new FanBelt();
    64. }
    65. }
    66. class GeneratorBelt extends Belt {
    67. @Override
    68. public GeneratorBelt get() {
    69. return new GeneratorBelt();
    70. }
    71. }
    72. class PowerSteeringBelt extends Belt {
    73. @Override
    74. public PowerSteeringBelt get() {
    75. return new PowerSteeringBelt();
    76. }
    77. }
    78. public class RegisteredFactories {
    79. public static void main(String[] args) {
    80. Stream.generate(new Part())
    81. .limit(10)
    82. .forEach(System.out::println);
    83. }
    84. }

            程序执行的结果是:

            先观察列表prototypes

    这些子类原本都应该由get()方法进行生成,它们的工厂类都被添加到了列表中,通过这种方式,我们完成了“注册”。这些工厂是对象本身的实例,在实际操作中,它们会作为生成其他对象的模板进行使用。

        这些对象就好比一个一个的工厂,它们会分别生产不同的“零件”。通过调用这些类的方法,我们就可以进行“零件”的生产。

            上述的FilterBelt用于对不同的“零件”进行分类,因此它们不需要具体的实现。

            这种做法的好处可以在main()方法中看见:由于Part类的get()方法可以生成各种各样的零件,我们仅需创建Part对象就可以控制各个“零件”的生产。

  • 相关阅读:
    HiSilicon352 android9.0 emmc添加新分区
    QT基础入门——界面布局和常用控件(四)
    sqoop-import 详解
    pytest-rerunfailures插件之测试用例失败重跑
    适用于多种场景功能强大的在线海报图片素材设计器源码
    第4季3:Hi3518e的sensor接口引脚复用设置
    RKMEDIA--VENC/VDEC使用
    随想录一刷Day44——动态规划
    【Go语言实战】(26) 分布式搜索引擎
    Broken pipe. The Gradle daemon may be trying to use ipv4 instead of ipv6.
  • 原文地址:https://blog.csdn.net/w_pab/article/details/134202707