使用Java :: Geci生成setter和getter

本文中,我们创建了非常简单的hello-world生成器,以介绍框架以及通常如何生成生成器。 在本文中,我们将研究访问器生成器,它是在Java :: Geci的核心模块中定义的,它是商业级的,而不是仅用于演示的生成器。 即使生成器是商业级的,使用框架的服务,生成器也具有简单的代码,因此可以在文章中表示。

访问器生成器有什么作用

访问器是设置器和获取器。 当一个类具有许多字段并且我们希望帮助封装时,我们将这些字段声明为private字段并创建setter和getter,每个字段一对,可以设置该字段的值(setter)并获取该字段的值(吸气剂)。 请注意,与许多初中生认为的相反,创建setter和getter本身并不是封装,而是可能是进行正确封装的工具。 同时请注意,它也可能不是正确封装的工具。 您可以在“ Joshua Bloch:有效的Java 3rd Edition”项目16中了解更多信息。

请谨慎阅读。 该书说它是为Java 9更新的。该Java版本包含模块系统。 Item 16一章没有提及,甚至本版本仍说要使用带有setter和getter的私有成员作为公共类,在Java 9的情况下,这也意味着模块中不会导出的包中的类。

许多开发人员认为,二传手和吸气剂本质上是邪恶的,并且是不良设计的标志。 不要犯错! 他们不主张直接使用原始字段。 那会更糟。 他们认为您应该以更加面向对象的思维方式进行编程。 在我看来,它们是正确的,并且按照我的专业实践,我必须使用许多类来维护旧版应用程序,这些类使用包含setter,getter的旧版框架来维护应用程序,这些旧版框架是应用程序周围的编程工具所必需的。 理论是一回事,现实生活是另一回事。 除非我们在添加新字段时忘记执行它们,否则不同的集成开发环境和许多其他工具(例如,生成器和获取器)会为我们生成。

设置器是一种方法,其参数与字段的类型相同,并返回void 。 (Aka不返回任何值。)按照约定set的名称,并使用首字母大写的字段名称。 对于字段businessOwner ,设置者通常是setBusinessOwner 。 设置器将字段的值设置为设置器的参数的值。

吸气剂也是一种不带任何参数但返回参数值的方法,因此它具有与字段类型相同的返回类型。 按照惯例,getter的名称是get ,再次是大写的字段名称。 这样,获取者将是getBusinessOwner

在的情况下, booleanBoolean类型fiels吸气剂具有is前缀,所以isBusinessOwner还可以的情况下,该领域是一些布尔类型的有效名称。

访问器会为其必须的所有字段生成setter和getter。

如何生成访问器

访问器生成器必须为该类的某些字段生成代码。 该生成器是Java :: Geci中过滤字段生成器的理想候选者。 过滤后的字段生成器扩展了AbstractFilteredFieldsGenerator类,并且它的process()方法为每个过滤后的字段调用一次。 除了几周前文章中看到的常规SourceCompoundParams参数之外,该方法还将Field作为第三个参数。

AbstractFilteredFieldsGenerator使用配置参数filter过滤字段。 这样,对于扩展此类的每个生成器,要考虑的字段的选择都是相同的,并且生成器不必关心字段过滤:它是为它们完成的。

生成器代码的主要部分如下:

 public class Accessor extends AbstractFilteredFieldsGenerator { 
    ...

    @Override

    public void process(Source source, Class<?> klass,

                        CompoundParams params,

                        Field field) throws Exception {

        final var id = params.get( "id" );

        source.init(id);

        var isFinal = Modifier.isFinal(field.getModifiers());

        var name = field.getName();

        var fieldType = GeciReflectionTools.typeAsString(field);

        var access = check(params.get( "access" , "public" ));

        var ucName = cap(name);

        var setter = params.get( "setter" , "set" + ucName);

        var getter = params.get( "getter" , "get" + ucName);

        var only = params.get( "only" );

        try (var segment = source.safeOpen(id)) {

            if (!isFinal && ! "getter" .equals(only)) {

                writeSetter(name, setter, fieldType, access, segment);

            }

            if (! "setter" .equals(only)) {

                writeGetter(name, getter, fieldType, access, segment);

            }

        }

    }
 }

省略号处的代码包含更多方法,我们将在后面介绍。 第一个调用是获取参数id 。 这是一个特殊参数,如果未定义,则默认params.get("id")返回是生成器的助记符。 这是唯一具有这样的全局默认值的参数。

source.init(id)的调用可确保即使生成器未向该段写入任何内容,该段也将被视为“已触摸”。 在某些情况下可能会发生这种情况,并且在编写生成器时,对于生成器要写入的任何段调用source.init(id)不会受到伤害。

该代码查看实际字段以检查该字段是否为最终字段。 如果该字段为final,则必须在创建对象时获取值,此后,设置器将无法对其进行修改。 在这种情况下,将仅为该字段创建一个吸气剂。

setter / getter生成器需要的下一项功能是字段名称,以及字段类型的字符串表示形式。 静态实用程序方法GeciReflectionTools.typeAsString()是框架中提供此功能的便捷工具。

可选的配置参数access将进入相同名称的变量,并且在setter和getter的access修饰符需要不同于public情况下将使用它。 默认值为public ,它被定义为方法params.get()的第二个参数。 方法check()是生成器的一部分。 它检查修饰符是否正确,并在大多数情况下阻止语法错误代码的生成(例如:使用访问修饰符pritected创建setter和getter)。 我们将在一段时间后研究该方法。

接下来的事情是getter和setter的名称。 默认情况下是set/get +字段的大写名称,但是也可以由配置参数settergetter定义。 这样,如果绝对需要,您可以拥有isBusinessOwner

最后一个配置参数only是密钥。 如果代码指定only='setter'only='getter'则仅生成setter或仅生成getter。

生成器要写入的段在try-with-resources块的开头打开,然后调用本地writeSetterwriteGetter方法。 有两种不同的方法可以从源对象打开细分。 一个正在调用open(id) ,另一个正在调用safeOpen(id) 。 第一种方法将尝试打开该段,如果在类源文件中未定义名称的段,则该方法将返回null 。 生成器可以检查无效性,并且如果进行了编程,则可以使用其他段名称。 另一方面,如果无法打开该段,则safeOpen()会引发GeciException 。 这是更安全的版本,可避免以后在生成器中出现空指针异常。 不太好。

请注意,仅当字段不是final且未将only配置键未配置为getter (仅)时,才写入setter。

让我们看一下这两种方法。 毕竟,这些是真正生成代码的生成器的真正核心方法。

 private static void writeGetter(String name, String getterName,

                                    String type, String access, Segment segment) {

        segment.write_r(access + " " + type + " " + getterName + "(){" )

                .write( "return " + name + ";" )

                .write_l( "}" )

                .newline();

    }

    private static void writeSetter(String name, String setterName,

                                    String type, String access, Segment segment) {

        segment.write_r(access + " void " + setterName + "(" +

                type + " " + name + "){" )

                .write( "this." + name + " = " + name + ";" )

                .write_l( "}" )

                .newline();

    }

这些方法获取字段的名称,访问者的名称,作为字符串的字段的类型,访问修饰符字符串以及代码必须写入的Segment 。 代码生成器不会直接写入源文件。 框架提供的segment对象用于发送生成的代码,如果需要,框架会将编写的行插入源代码中。

该段的write()write_l()write_r()方法可用于编写代码。 如果有多个参数,它们的工作方式与String.format非常相似,但是它们也关心适当的制表。 当代码调用write_r()该段将记住,其后的行必须在表格的右边write_r()四个空格。 当代码调用write_l()该段知道制表必须减少四个字符(即使对于实际的写入行也是如此)。 它们还处理多行字符串,以便将它们全部正确制成表格。

生成的代码也应该可读。

最后的简单方法是访问修饰符检查。

 private static final Set<String> accessModifiers =

            Set.of( "public" , "private" , "protected" , "package" );
 ...

    private String check( final String access) {

        if (!access.endsWith( "!" ) && !accessModifiers.contains(access)) {

            throw new GeciException( "'" +access+ "' is not a valid access modifier" );

        }

        final String modifiedAccess;

        if ( access.endsWith( "!" )){

            modifiedAccess = access.substring( 0 ,access.length()- 1 );

        } else {

            modifiedAccess = access;

        }

        if ( modifiedAccess.equals( "package" )){

            return "" ;

        }

        return modifiedAccess;

    }

进行此检查的目的是防止程序员错误地访问access修饰符。 它检查access修饰符是private (虽然我看不到这个的实际用例), protectedpublic还是package 。 最后一个转换为空字符串,因为包保护的访问是类方法的默认设置。 同时在配置中使用空字符串表示程序包的私有访问不是真正可读的。

这样,如果配置pritected包含拼写错误的代码,则代码生成器将抛出异常,并拒绝生成已知包含语法错误的代码。 另一方面,访问修饰符也可以更复杂。 在极少数情况下,该程序可能需要同步的吸气剂和吸气剂。 我们不会试图自动找出类似检查字段是否可变的内容,因为这些都是边界情况。 但是,生成器提供了克服有限语法检查的可能性,并且仅提供任何字符串作为访问修饰符即可。 如果访问修饰符字符串以感叹号结尾,则表示使用生成器的程序员对访问修饰符的正确性承担全部责任,并且生成器将按原样使用它(当然不带感叹号)。

剩下的是mnemoniccap方法:

 private static String cap(String s) {

        return s.substring( 0 , 1 ).toUpperCase() + s.substring( 1 );

    }

    @Override

    public String mnemonic() {

        return "accessor" ;

    }

框架使用mnemonic()方法来标识需要此生成器服务的源,并将其用作配置参数id的默认值。 所有生成器都应提供此功能。 另一种是cap的是大写的字符串。 我不会解释它是如何工作的。

样品使用

 @Geci ( "accessor filter='private | protected'" )
 public class Contained1 { 
    public void callMe() { 
    }

    private final String apple = "" ;

    @Geci ( "accessors only='setter'" )

    private int birnen; 
    int packge; 
    @Geci ( "accessor access='package' getter='isTrue'" )

    protected boolean truth;

    @Geci ( "accessor filter='false'" )

    protected int not_this; 
    public Map<String,Set<Map<Integer,Boolean>>> doNothingReally( int a, Map b, Set<Set> set){

        return null ;

    }

    //<editor-fold id="accessor" desc="setters">

    //</editor-fold>
 }

该类使用Geci注释进行注释。 参数为accessor filter='private | protected' accessor filter='private | protected' ,它定义了要在此源文件上使用的生成器的名称并配置了过滤器。 它说,我们需要私有和受保护字段的设置者和获取者。 逻辑表达式应为:“过滤字段是私有的还是受保护的”。

一些字段也有注释。 birnen将得到的只是一个二传手, truth setter和getter将被封装保护剂和吸气将被命名为isTrue() 字段not_this不会获得setter或getter,因为在字段注释中覆盖了过滤器表达式,并说: false永远不会为true ,需要生成器进行处理。

未注释字段apple ,它将根据类级别的配置进行处理。 它是私有的,因此将获得访问器,并且由于它是final因此将仅获得吸气剂。

之间的代码

 // <editor- fold id = "accessor" desc= "setters" > 
    // < /editor-fold >

将包含生成的代码。 (您必须运行代码才能看到它,我没有在这里复制它。)

概要

在本文中,我们研究了一个生成器,它是Java :: Geci框架中真实的商业级生成器。 遍历代码,我们讨论了代码的工作方式,还讨论了编写代码生成器的其他一些更一般的方面。 下一步是使用Java :: Geci作为测试依赖项启动一个项目,使用访问器生成器而不是IDE代码生成器(这样您就可以忘记重新执行setter getter生成),以后,也许可以创建您的拥有生成器,而不仅是设置器和获取器,还可以执行更复杂的任务。

翻译自: https://www.javacodegeeks.com/2019/06/generating-setters-and-getters-using-javageci.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值