十九、类型信息(4)

news2024/11/18 21:39:03

本章概要

  • 注册工厂
  • 类的等价比较
  • 反射:运行时类信息
    • 类方法提取器

注册工厂

Pet 层次结构生成对象的问题是,每当向层次结构中添加一种新类型的 Pet 时,必须记住将其添加到 LiteralPetCreator.java 的条目中。在一个定期添加更多类的系统中,这可能会成为问题。

你可能会考虑向每个子类添加静态初始值设定项,因此初始值设定项会将其类添加到某个列表中。不幸的是,静态初始值设定项仅在首次加载类时调用,因此存在鸡和蛋的问题:生成器的列表中没有类,因此它无法创建该类的对象,因此类不会被加载并放入列表中。

基本上,你必须自己手工创建列表(除非你编写了一个工具来搜索和分析源代码,然后创建和编译列表)。所以你能做的最好的事情就是把列表集中放在一个明显的地方。层次结构的基类可能是最好的地方。

我们在这里所做的另一个更改是使用_工厂方法_设计模式将对象的创建推迟到类本身。工厂方法可以以多态方式调用,并为你创建适当类型的对象。事实证明,java.util.function.SupplierT get() 描述了原型工厂方法。协变返回类型允许 get()Supplier 的每个子类实现返回不同的类型。

在本例中,基类 Part 包含一个工厂对象的静态列表,列表成员类型为 Supplier<Part>。对于应该由 get() 方法生成的类型的工厂,通过将它们添加到 prototypes 列表向基类“注册”。奇怪的是,这些工厂本身就是对象的实例。此列表中的每个对象都是用于创建其他对象的_原型_:

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

// 注册工厂到基础类
class Part implements Supplier<Part> {
    @Override
    public String toString() {
        return getClass().getSimpleName();
    }

    static List<Supplier<? extends Part>> prototypes =
            Arrays.asList(
                    new FuelFilter(),
                    new AirFilter(),
                    new CabinAirFilter(),
                    new OilFilter(),
                    new FanBelt(),
                    new PowerSteeringBelt(),
                    new GeneratorBelt()
            );

    private static Random rand = new Random(47);

    @Override
    public Part get() {
        int n = rand.nextInt(prototypes.size());
        return prototypes.get(n).get();
    }
}

class Filter extends Part {
}

class FuelFilter extends Filter {
    @Override
    public FuelFilter get() {
        return new FuelFilter();
    }
}

class AirFilter extends Filter {
    @Override
    public AirFilter get() {
        return new AirFilter();
    }
}

class CabinAirFilter extends Filter {
    @Override
    public CabinAirFilter get() {
        return new CabinAirFilter();
    }
}

class OilFilter extends Filter {
    @Override
    public OilFilter get() {
        return new OilFilter();
    }
}

class Belt extends Part {
}

class FanBelt extends Belt {
    @Override
    public FanBelt get() {
        return new FanBelt();
    }
}

class GeneratorBelt extends Belt {
    @Override
    public GeneratorBelt get() {
        return new GeneratorBelt();
    }
}

class PowerSteeringBelt extends Belt {
    @Override
    public PowerSteeringBelt get() {
        return new PowerSteeringBelt();
    }
}

public class RegisteredFactories {
    public static void main(String[] args) {
        Stream.generate(new Part())
                .limit(10)
                .forEach(System.out::println);
    }
}

输出结果:

在这里插入图片描述

并非层次结构中的所有类都应实例化;这里的 FilterBelt 只是分类器,这样你就不会创建任何一个类的实例,而是只创建它们的子类(请注意,如果尝试这样做,你将获得 Part 基类的行为)。

因为 Part implements Supplier<Part>Part 通过其 get() 方法供应其他 Part。如果为基类 Part 调用 get()(或者如果 generate() 调用 get()),它将创建随机特定的 Part 子类型,每个子类型最终都从 Part 继承,并重写相应的 get() 以生成它们中的一个。

类的等价比较

当你查询类型信息时,需要注意:instanceof 的形式(即 instanceofisInstance() ,这两者产生的结果相同) 和 与 Class 对象直接比较 这两者间存在重要区别。下面的例子展示了这种区别:

// instanceof 与 class 的差别
class Base {
}

class Derived extends Base {
}

public class FamilyVsExactType {
    static void test(Object x) {
        System.out.println("Testing x of type " + x.getClass());
        System.out.println("x instanceof Base " + (x instanceof Base));
        System.out.println("x instanceof Derived " + (x instanceof Derived));
        System.out.println("Base.isInstance(x) " + Base.class.isInstance(x));
        System.out.println("Derived.isInstance(x) " + Derived.class.isInstance(x));
        System.out.println("x.getClass() == Base.class " + (x.getClass() == Base.class));
        System.out.println("x.getClass() == Derived.class " + (x.getClass() == Derived.class));
        System.out.println("x.getClass().equals(Base.class)) " + (x.getClass().equals(Base.class)));
        System.out.println("x.getClass().equals(Derived.class)) " + (x.getClass().equals(Derived.class)));
    }

    public static void main(String[] args) {
        test(new Base());
        test(new Derived());
    }
}

输出结果:

在这里插入图片描述

test() 方法使用两种形式的 instanceof 对其参数执行类型检查。然后,它获取 Class 引用,并使用 ==equals() 测试 Class 对象的相等性。令人放心的是,instanceofisInstance() 产生的结果相同, equals()== 产生的结果也相同。但测试本身得出了不同的结论。与类型的概念一致,instanceof 说的是“你是这个类,还是从这个类派生的类?”。而如果使用 == 比较实际的Class 对象,则与继承无关 —— 它要么是确切的类型,要么不是。

反射:运行时类信息

如果你不知道对象的确切类型,RTTI 会告诉你。但是,有一个限制:必须在编译时知道类型,才能使用 RTTI 检测它,并对信息做一些有用的事情。换句话说,编译器必须知道你使用的所有类。

起初,这看起来并没有那么大的限制,但是假设你引用了一个不在程序空间中的对象。实际上,该对象的类在编译时甚至对程序都不可用。也许你从磁盘文件或网络连接中获得了大量的字节,并被告知这些字节代表一个类。由于这个类在编译器为你的程序生成代码后很长时间才会出现,你如何使用这样的类?

在传统编程环境中,这是一个牵强的场景。但是,当我们进入一个更大的编程世界时,会有一些重要的情况发生。第一个是基于组件的编程,你可以在应用程序构建器_集成开发环境_中使用_快速应用程序开发_(RAD)构建项目。这是一种通过将表示组件的图标移动到窗体上来创建程序的可视化方法。然后,通过在编程时设置这些组件的一些值来配置这些组件。

这种设计时配置要求任何组件都是可实例化的,它公开自己的部分,并且允许读取和修改其属性。此外,处理_图形用户界面_(GUI)事件的组件必须公开有关适当方法的信息,以便 IDE 可以帮助程序员覆写这些事件处理方法。反射提供了检测可用方法并生成方法名称的机制。

在运行时发现类信息的另一个令人信服的动机是提供跨网络在远程平台上创建和执行对象的能力。这称为_远程方法调用_(RMI),它使 Java 程序的对象分布在许多机器上。这种分布有多种原因。如果你想加速一个计算密集型的任务,你可以把它分解成小块放到空闲的机器上。或者你可以将处理特定类型任务的代码(例如,多层次客户机/服务器体系结构中的“业务规则”)放在特定的机器上,这样机器就成为描述这些操作的公共存储库,并且可以很容易地更改它以影响系统中的每个人。分布式计算还支持专门的硬件,这些硬件可能擅长于某个特定的任务——例如矩阵转换——但对于通用编程来说不合适或过于昂贵。

Class 支持_反射_的概念, java.lang.reflect 库中包含类 FieldMethodConstructor(每一个都实现了 Member 接口)。这些类型的对象由 JVM 在运行时创建,以表示未知类中的对应成员。

然后,可以使用 Constructor 创建新对象,get()set() 方法读取和修改与 Field 对象关联的字段,invoke() 方法调用与 Method 对象关联的方法。此外,还可以调用便利方法 getFields()getMethods()getConstructors() 等,以返回表示字段、方法和构造函数的对象数组。(你可以通过在 JDK 文档中查找类 Class 来了解更多信息。)因此,匿名对象的类信息可以在运行时完全确定,编译时不需要知道任何信息。

重要的是要意识到反射没有什么魔力。当你使用反射与未知类型的对象交互时,JVM 将查看该对象,并看到它属于特定的类(就像普通的 RTTI)。在对其执行任何操作之前,必须加载 Class 对象。因此,该特定类型的 .class 文件必须在本地计算机上或通过网络对 JVM 仍然可用。因此,RTTI 和反射的真正区别在于,使用 RTTI 时,编译器在编译时会打开并检查 .class 文件。换句话说,你可以用“正常”的方式调用一个对象的所有方法。通过反射,.class 文件在编译时不可用;它由运行时环境打开并检查。

类方法提取器

通常,你不会直接使用反射工具,但它们可以帮助你创建更多的动态代码。反射是用来支持其他 Java 特性的,例如对象序列化。但是,有时动态提取有关类的信息很有用。

考虑一个类方法提取器。查看类定义的源代码或 JDK 文档,只显示_在该类定义中_定义或重写的方法。但是,可能还有几十个来自基类的可用方法。找到它们既单调又费时。幸运的是,反射提供了一种方法,可以简单地编写一个工具类自动地向你展示所有的接口:

import java.lang.reflect.*;
import java.util.regex.*;

// 使用反射展示一个类的所有方法,甚至包括定义在基类中方法
public class ShowMethods {
    private static String usage =
            "usage:\n" +
                    "ShowMethods qualified.class.name\n" +
                    "To show all methods in class or:\n" +
                    "ShowMethods qualified.class.name word\n" +
                    "To search for methods involving 'word'";
    private static Pattern p = Pattern.compile("\\w+\\.");

    public static void main(String[] args) {
        if (args.length < 1) {
            System.out.println(usage);
            System.exit(0);
        }
        int lines = 0;
        try {
            Class<?> c = Class.forName(args[0]);
            Method[] methods = c.getMethods();
            Constructor[] ctors = c.getConstructors();
            if (args.length == 1) {
                for (Method method : methods) {
                    System.out.println(
                            p.matcher(method.toString()).replaceAll(""));
                }
                for (Constructor ctor : ctors) {
                    System.out.println(p.matcher(ctor.toString()).replaceAll(""));
                }
                lines = methods.length + ctors.length;
            } else {
                for (Method method : methods) {
                    if (method.toString().contains(args[1])) {
                        System.out.println(p.matcher(method.toString()).replaceAll(""));
                        lines++;
                    }
                }
                for (Constructor ctor : ctors) {
                    if (ctor.toString().contains(args[1])) {
                        System.out.println(p.matcher(ctor.toString()).replaceAll(""));
                        lines++;
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            System.out.println("No such class: " + e);
        }
    }
}

在这里插入图片描述

输出结果:

在这里插入图片描述

Class 方法 getmethods()getconstructors() 分别返回 Method 数组和 Constructor 数组。这些类中的每一个都有进一步的方法来解析它们所表示的方法的名称、参数和返回值。但你也可以像这里所做的那样,使用 toString(),生成带有整个方法签名的 String。代码的其余部分提取命令行信息,确定特定签名是否与目标 String(使用 indexOf())匹配,并使用正则表达式删除名称限定符。

编译时无法知道 Class.forName() 生成的结果,因此所有方法签名信息都是在运行时提取的。如果你研究 JDK 反射文档,你将看到有足够的支持来实际设置和对编译时完全未知的对象进行方法调用。虽然最初你可能认为你永远都不需要这样做,但是反射的全部价值可能会令人惊讶。

上面的输出来自命令行:

com.example.test.ShowMethods

输出包含一个 public 无参数构造函数,即使未定义构造函数。你看到的构造函数是由编译器自动合成的。如果将 ShowMethods 设置为非 public 类(即只有包级访问权),则合成的无参数构造函数将不再显示在输出中。自动为合成的无参数构造函数授予与类相同的访问权。

尝试运行 java ShowMethods java.lang.String,并附加一个 charintString 等参数。

编程时,当你不记得某个类是否有特定的方法,并且不想在 JDK 文档中搜索索引或类层次结构时,或者如果你不知道该类是否可以对 Color 对象执行任何操作时,该工具能节省不少时间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1155436.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

锁表后引发的几种删除方式与不同的扩展

在开发过程可能会遇到一些特殊场景&#xff0c;诸如我想删除某表&#xff0c;但是无法删除&#xff0c;去找原因发现是发生了锁表&#xff0c; 锁表指的是在执行一个事务时&#xff0c;该事务获取了一个锁并保持其锁定状态&#xff0c;直到事务完成或手动释放锁&#xff0c;导…

【零参考GAN:Pansharpening】

ZeRGAN: Zero-Reference GAN for Fusion of Multispectral and Panchromatic Images &#xff08;用于多光谱和全色图像融合的零参考GAN&#xff09; 本文提出了一种融合低空间分辨率多光谱(LR MS)和高空间分辨率全色(PAN)图像的新的全色锐化方法–零参考生成对抗网络(ZeRGAN…

深度学习之基于ResNet18的神经网络水果分类系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介二、功能三、神经网络水果分类系统四. 总结 一项目简介 基于ResNet18神经网络的水果分类系统是一个利用深度学习技术进行水果图像分类的系统。下面是该系统…

WOS与CNKI数据库的citespace分析教程及常见问题解决

本教程为面向新手的基于citespace的数据可视化教程&#xff0c;旨在帮助大家更快了解行业前沿的研究内容。 获取最新版本的citespace软件 在citespace官网下载最新的版本&#xff08;如果是老版本&#xff0c;可能会提示让你去官网更新为最新版&#xff0c;老版本不再提供服务…

【Linux】开发工具——vim多模式编辑器的入土设置sudoers白名单

个人主页点击直达&#xff1a;小白不是程序媛 Linux系列专栏&#xff1a;Linux被操作记 目录 前言&#xff1a; 基本概念 vim基本操作 [正常模式]切换至[插入模式] [插入模式]切换至[正常模式] [正常模式]切换至[末行模式] 三种模式的切换关系图 vim命令模式命令集 进…

Redis入门01-简单了解

目录 Redis的发展历史 特性简介 数据类型 内存存储与持久化 功能丰富 简单稳定 应用场景 为啥用Redis Redis的发展历史 Redis&#xff08;Remote Dictionary Server&#xff09;是一个高性能的键值存储系统&#xff0c;通常用作缓存、消息队列和分布式数据存储的解决方…

【React】03.脚手架的进阶应用

文章目录 暴露webpack配置暴露前后的区别config文件夹&#xff1a;scripts文件夹&#xff1a;package.json 常见的配置修改1.把sass改为less2.配置别名3.修改域名和端口号4.修改浏览器兼容5.处理Proxy跨域 2023年最新珠峰React全家桶【react基础-进阶-项目-源码-淘系-面试题】 …

uniapp如何使用mumu模拟器

模拟器安装 下载地址&#xff1a;MuMu模拟器 模拟器相关设置 1.在设置-显示中选中手机版&#xff0c;设置手机分辨率 2.设置-关于手机-版本号快速点击&#xff0c;将其设置为开发者模式 3.选择多开器 4.打开hbuilderx&#xff0c;找到adb设置 5.配置adb路径及端口号&#x…

网络爬虫适合什么代理IP?如何使用?

在互联网时代之下&#xff0c;大数据对各行各业的发展有着重要的推动作用&#xff0c;而说到数据采集&#xff0c;必不可少的就是去使用爬虫工作。 一、什么是网络爬虫&#xff1f; 它是一种按照一定的规则自动游览、检索网页信息的程序或者脚本&#xff0c;通过自动请求目标…

《鸿门宴》文

鸿门宴 【作者】司马迁 【朝代】汉 沛公军霸上&#xff0c;未得与项羽相见。沛公左司马曹无伤使人言于项羽曰&#xff1a;“沛公欲王关中&#xff0c;使子婴为相&#xff0c;珍宝尽有之。”项羽大怒曰&#xff1a;“旦日飨士卒&#xff0c;为击破沛公军&#xff01;”当是时&a…

SpringCloud Gateway实现请求解密和响应加密

文章目录 前言正文一、项目简介二、核心代码2.1 自定义过滤器2.2 网关配置2.3 自定义配置类2.4 加密组件接口2.5 加密组件实现&#xff0c;AES算法2.6 启动类&#xff0c;校验支持的算法配置 三、请求报文示例四、测试结果4.1 网关项目启动时4.2 发生请求时 前言 本文环境使用比…

【C++的OpenCV】第十五课-OpenCV的绘图工具(rectangle、circle、line、polylines、putText)常用方法简介

&#x1f389;&#x1f389;&#x1f389; 欢迎各位来到小白 p i a o 的学习空间&#xff01; \color{red}{欢迎各位来到小白piao的学习空间&#xff01;} 欢迎各位来到小白piao的学习空间&#xff01;&#x1f389;&#x1f389;&#x1f389; &#x1f496; C\Python所有的入…

【错误解决方案】ModuleNotFoundError: No module named ‘xgboost‘

1. 错误提示 在尝试导入名为xgboost的模块时出现了ModuleNotFoundError。 错误提示&#xff1a;ModuleNotFoundError: No module named xgboost 这个错误通常意味着Python环境中没有安装你试图导入的模块。 2. 解决方案 安装xgboost模块即可解决上述问题。 可以通过Python…

企业多部门VLAN间互访部署实战

1. 二层VLAN技术部署回顾&#xff1b; 2. 三层交换机上如何部署VLAN&#xff1f; 3. 部署VLAN的过程中有哪些注意事项&#xff1f; -- VLAN技术 - 虚拟局域网 -- 局域网 - 通过交换机连接的网络 -- VLAN - 把一个大的局域网 做一个分割 -- 常见局域网的问题&a…

Docker容器引擎

目录 一、Docker概述 二、Docker与虚拟机的区别 三、namespace 四、Docker核心概念 五、Docker部署 一、Docker概述 Docker是一个开源的应用容器引擎&#xff0c;基于go语言开发并遵循了apache2.0协议开源。 Docker是在Linux容器里运行应用的开源工具&#xff0c;是一种轻…

10.2 一文读懂SPI与DSPI、QSPI、OSPI关系与异同

本文主要内容: 1 SPI与DSPI、QSPI、OSPI定义 2 SPI与xSPI对比 3 常用的nor flash 4 驱动架构 5 xSPI镜像烧录 1 SPI与DSPI、QSPI、OSPI定义 1)标准SPI 通过说的SPI,称为标准SPI,是一种串行外设接口,通过有4根线控制,CLK、CS、MISO、MOSI,可工作于4种模式,一般是主机…

透视2023,如何看清中国SaaS的未来之路?

导读&#xff1a;什么是更适合中国市场的SaaS道路&#xff1f; 如果用一个关键词概括2023年的SaaS产业&#xff0c;很多人会想到&#xff1a;难。 在过去一年时间内&#xff0c;SaaS产业投融资环境巨变&#xff0c;一级市场投融资笔数和金额骤减。根据IT桔子数据&#xff0c;20…

搭上直播快车,文旅迎来了更大爆发期?

“直播累计观看人数1083万人次&#xff0c;同期在线峰值10万人&#xff0c;抖音平台销售额800万元&#xff0c;荣登食遍天下榜第一名”。 10月28日&#xff0c;“东方甄选看世界”无锡专场直播落幕&#xff0c;又创造了新成绩&#xff0c;“文旅直播”这一新带货模式的发展可行…

飞鹅打印机使用注意事项:打印小票(云播报打印机)FP-V58-W(c)

文章目录 引言I 基础操作1.1 设置Wi-Fi1.2 在机器内预先内置logo 引言 应用场景&#xff1a; 云播报打印机&#xff1a;支持第三方软件开发商&#xff0c;接单后实现智能语音播报&#xff0c;可播报订单信息、打印订单小票。 http://www.feieyun.com/open/index.html 飞鹅对…

实用的文案生成工具、数字人生成工具、ai配音生成、音效下载、图片颜色读取器、自动生成logo 在线网站【持续更新】

一、文案生成工具 传送门 传送门 二、数字人 传送门 三、ai朗读 真人付费 传送门 传送门 四、音效下载 传送门 五、图片颜色读取器 传送门 六、自动生成logo 传送门 七、图片转 BASE64 传送门 ps:pr绿幕扣除 效果中搜索超级键 2. 拖动到轨道中 3. 点击左边主…