Builder使用创建者模式又叫建造者模式。简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程。基础使用 Builder注释为你的类生成相对略微复杂的构建器API。Builder可以让你以下面显示的那样调用你的代码,来初始化你的实例对象:Student。builder()。sno(001)。sname(admin)。sage(18)。sphone(110)。build(); Builder可以放在类,构造函数或方法上。虽然放在类上和放在构造函数上这两种模式是最常见的用例,但Builder最容易用放在方法的用例来解释。那么Builder内部帮我们做了什么?创建一个名为ThisClassBuilder的内部静态类,并具有和实体类形同的属性(称为构建器)。在构建器中:对于目标类中的所有的属性和未初始化的final字段,都会在构建器中创建对应属性。在构建器中:创建一个无参的default构造函数。在构建器中:对于实体类中的每个参数,都会对应创建类似于setter的方法,只不过方法名与该参数名相同。并且返回值是构建器本身(便于链式调用),如上例所示。在构建器中:一个build()方法,调用此方法,就会根据设置的值进行创建实体对象。在构建器中:同时也会生成一个toString()方法。在实体类中:会创建一个builder()方法,它的目的是用来创建构建器。 说这么多,不如让我们通过下面这个例子来理解BuilderpublicclassUser{privatefinalIntegercode200;privateStringusername;privateStringpassword;}编译后:publicclassUser{privateStringusername;privateStringpassword;User(Stringusername,Stringpassword){this。usernameusername;this。passwordpassword;}publicstaticUser。UserBuilderbuilder(){returnnewUser。UserBuilder();}publicstaticclassUserBuilder{privateStringusername;privateStringpassword;UserBuilder(){}publicUser。UserBuilderusername(Stringusername){this。usernameusername;returnthis;}publicUser。UserBuilderpassword(Stringpassword){this。passwordpassword;returnthis;}publicUserbuild(){returnnewUser(this。username,this。password);}publicStringtoString(){returnUser。UserBuilder(usernamethis。username,passwordthis。password);}}}组合用法 Builder中使用Singular注释集合 Builder也可以为集合类型的参数或字段生成一种特殊的方法。它采用修改列表中一个元素而不是整个列表的方式,可以是增加一个元素,也可以是删除一个元素。Student。builder()。sno(001)。sname(admin)。sage(18)。sphone(110)。sphone(112)。build(); 这样就可以轻松地将List字段中包含2个字符串。但是想要这样来操作集合,你需要使用Singular来注释字段或参数。23种设计模式实战(很全)分享给你们。 在使用Singular注释注释一个集合字段(使用Builder注释类),lombok会将该构建器节点视为一个集合,并生成两个adder方法而不是setter方法。一个向集合添加单个元素一个将另一个集合的所有元素添加到集合中 将不生成仅设置集合(替换已添加的任何内容)的setter。还生成了clear方法。这些singular构建器相对而言是有些复杂的,主要是来保证以下特性:在调用build()时,生成的集合将是不可变的。在调用build()之后调用其中一个adder方法或clear方法不会修改任何已经生成的对象。如果集合修改之后,再调用build(),则会创建一个基于上一个对象创建的对象实体。生成的集合将被压缩到最小的可行格式,同时保持高效。 Singular只能应用于lombok已知的集合类型。目前,支持的类型有: java。util:Iterable,Collection,和List(一般情况下,由压缩的不可修改的ArrayList支持)。Set,SortedSet,andNavigableSet(一般情况下,生成可变大小不可修改的HashSet或者TreeSet)。Map,SortedMap,andNavigableMap(一般情况下,生成可变大小不可修改的HashMap或者TreeMap)。Guava’scom。google。common。collect:ImmutableCollectionandImmutableListImmutableSetandImmutableSortedSetImmutableMap,ImmutableBiMap,andImmutableSortedMapImmutableTable 来看看使用了Singular注解之后的编译情况:BuilderpublicclassUser{privatefinalIntegerid;privatefinalStringzipCode123456;privateStringusername;privateStringpassword;SingularprivateListStringhobbies;}编译后:publicclassUser{privatefinalIntegerid;privatefinalStringzipCode123456;privateStringusername;privateStringpassword;privateListStringhobbies;User(Integerid,Stringusername,Stringpassword,ListStringhobbies){this。idid;this。usernameusername;this。passwordpassword;this。hobbieshobbies;}publicstaticUser。UserBuilderbuilder(){returnnewUser。UserBuilder();}publicstaticclassUserBuilder{privateIntegerid;privateStringusername;privateStringpassword;privateArrayListStringhobbies;UserBuilder(){}publicUser。UserBuilderid(Integerid){this。idid;returnthis;}publicUser。UserBuilderusername(Stringusername){this。usernameusername;returnthis;}publicUser。UserBuilderpassword(Stringpassword){this。passwordpassword;returnthis;}publicUser。UserBuilderhobby(Stringhobby){if(this。hobbiesnull){this。hobbiesnewArrayList();}this。hobbies。add(hobby);returnthis;}publicUser。UserBuilderhobbies(Collectionlt;?extendsStringhobbies){if(this。hobbiesnull){this。hobbiesnewArrayList();}this。hobbies。addAll(hobbies);returnthis;}publicUser。UserBuilderclearHobbies(){if(this。hobbies!null){this。hobbies。clear();}returnthis;}publicUserbuild(){Listhobbies;switch(this。hobbiesnull?0:this。hobbies。size()){case0:hobbiesCollections。emptyList();break;case1:hobbiesCollections。singletonList(this。hobbies。get(0));break;default:hobbiesCollections。unmodifiableList(newArrayList(this。hobbies));}returnnewUser(this。id,this。username,this。password,hobbies);}publicStringtoString(){returnUser。UserBuilder(idthis。id,usernamethis。username,passwordthis。password,hobbiesthis。hobbies);}}} 其实,lombok的创作者还是很用心的,在进行build()来创建实例对象时,并没有直接使用Collections。unmodifiableList(Collection)此方法来创建实例,而是分为三种情况。第一种,当集合中没有元素时,创建一个空间list第二种情况,当集合中存在一个元素时,创建一个不可变的单元素list第三种情况,根据当前集合的元素数量创建对应合适大小的list 当然我们看编译生成的代码,创建了三个关于集合操作的方法:hobby(Stringhobby):向集合中添加一个元素hobbies(Collectionlt;?extendsStringhobbies):添加一个集合所有的元素clearHobbies():清空当前集合数据 Singular注解配置value属性 Java核心技术专注分享Java核心技术干货,包括Java多线程、IO、JVM、SpringBoot、SpringCloud、IntelliJIDEA、Dubbo、Zookeeper、Redis、架构、微服务、消息队列、Git、面试题、最新动态等。15篇原创内容 公众号 我们先来看看Singular注解的详情:Target({FIELD,PARAMETER})Retention(SOURCE)publicinterfaceSingular{修改添加集合元素的方法名Stringvalue()default;} 测试如何使用注解属性valueBuilderpublicclassUser{privatefinalIntegerid;privatefinalStringzipCode123456;privateStringusername;privateStringpassword;Singular(valuetestHobbies)privateListStringhobbies;}测试类publicclassBuilderTest{publicstaticvoidmain(String〔〕args){UseruserUser。builder()。testHobbies(reading)。testHobbies(eat)。id(1)。password(admin)。username(admin)。build();System。out。println(user);}} 说明,当我们使用了注解属性value之后,我们在使用添加集合元素时的方法会发生相应的改变。但是,同时生成的添加整个集合的方法名发生改变了吗?我们再来看看编译后的代码:编译后:publicclassUser{省略部分代码,只看关键部分publicstaticclassUserBuilder{publicUser。UserBuildertestHobbies(StringtestHobbies){if(this。hobbiesnull){this。hobbiesnewArrayList();}this。hobbies。add(testHobbies);returnthis;}publicUser。UserBuilderhobbies(Collectionlt;?extendsStringhobbies){if(this。hobbiesnull){this。hobbiesnewArrayList();}this。hobbies。addAll(hobbies);returnthis;}publicUser。UserBuilderclearHobbies(){if(this。hobbies!null){this。hobbies。clear();}returnthis;}}} 可以看到,只有添加一个元素的方法名发生了改变。最新面试题整理好了,点击Java面试库小程序在线刷题。 Builder。Default的使用 比如有这样一个实体类:BuilderToStringpublicclassUser{Builder。DefaultprivatefinalStringidUUID。randomUUID()。toString();privateStringusername;privateStringpassword;Builder。DefaultprivatelonginsertTimeSystem。currentTimeMillis();} 在类中我在id和insertTime上都添加注解Builder。Default,当我在使用这个实体对象时,我就不需要在为这两个字段进行初始化值,如下面这样:publicclassBuilderTest{publicstaticvoidmain(String〔〕args){UseruserUser。builder()。password(admin)。username(admin)。build();System。out。println(user);}}输出内容:User(id416219e1bc6443fdb2c39f8dc109c2e8,usernameadmin,passwordadmin,insertTime1546869309868) lombok在实例化对象时就为我们初始化了这两个字段值。 当然,你如果再对这两个字段进行设置置的话,那么默认定义的值将会被覆盖掉,如下面这样:publicclassBuilderTest{publicstaticvoidmain(String〔〕args){UseruserUser。builder()。id(admin)。password(admin)。username(admin)。build();System。out。println(user);}}输出内容User(idadmin,usernameadmin,passwordadmin,insertTime1546869642151) Builder详细配置 下面我们再来详细看看Builder这个注解类地详细实现:Target({TYPE,METHOD,CONSTRUCTOR})Retention(SOURCE)publicinterfaceBuilder{如果Builder注解在类上,可以使用Builder。Default指定初始化表达式Target(FIELD)Retention(SOURCE)publicinterfaceDefault{}指定实体类中创建Builder的方法的名称,默认为:builder(个人觉得没必要修改)StringbuilderMethodName()defaultbuilder;指定Builder中用来构件实体类的方法的名称,默认为:build(个人觉得没必要修改)StringbuildMethodName()defaultbuild;指定创建的建造者类的名称,默认为:实体类名BuilderStringbuilderClassName()default;使用toBuilder可以实现以一个实例为基础继续创建一个对象。(也就是重用原来对象的值)booleantoBuilder()defaultfalse;Target({FIELD,PARAMETER})Retention(SOURCE)publicinterfaceObtainVia{告诉lombok使用表达式获取值Stringfield()default;告诉lombok使用表达式获取值Stringmethod()default;booleanisStatic()defaultfalse;}} 以上注解属性,我只测试一个比较常用的toBuilder,因为我们在对实体对象进行操作时,往往会存在对某些实体对象的某个字段进行二次赋值,这个时候就会用到这一属性。最新面试题整理好了,点击Java面试库小程序在线刷题。 但是,这会创建一个新的对象,而不是原来的对象,原来的对象属性是不可变的,除非你自己想要给这个实体类再添加上Data或者setter方法。下面就来测试一下:Builder(toBuildertrue)ToStringpublicclassUser{privateStringusername;privateStringpassword;}测试类publicclassBuilderTest{publicstaticvoidmain(String〔〕args){Useruser1User。builder()。password(admin)。username(admin)。build();System。out。println(user1);Useruser2user1。toBuilder()。username(admin2)。build();验证user2是否是基于user1的现有属性创建的System。out。println(user2);验证对象是否是同一对象System。out。println(user1user2);}}输出内容User(usernameadmin,passwordadmin)User(usernameadmin2,passwordadmin)false Builder全局配置是否禁止使用Builderlombok。builder。flagUsage〔warningerror〕(default:notset)是否使用Guaualombok。singular。useGuava〔truefalse〕(default:false)是否自动使用singular,默认是使用lombok。singular。auto〔truefalse〕(default:true) 总的来说Builder还是很好用的。