专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

谁说Java不能多继承!

  从今以后,只要谁说Java不能多继承
  我都会说,是的没错(秒怂)
  要不你再看看标题写了啥?
  没毛病啊,你说Java不能多继承,我也说Java不能多继承
  这不是巧了么,没想到我们对一件事物的看法竟如此一致,看来这就是猿粪啊此继承非彼继承
  那你这又是唱哪出?
  直接上图!
  可以看到当我们在B类上添加注解InheritClass并指定A1。class和A2。class之后,我们的B实例就有了A1和A2的属性和方法
  就好像B同时继承了A1和A2
  这难道是黑魔法?(为什么脑子里会突然冒出来巴啦啦能量?)
  来人,把。class文件带上来
  其实就是把A1和A2的属性和方法都复制到了B上,和继承没有半毛钱关系!
  这玩意儿有啥用
  说起来现在实现的功能和当初的目的还是有点出入的
  众所周知,Lombok中提供了Builder的注解来生成一个类对应的Builder
  但是我想在build之前校验某些字段就不太好实现
  于是我就考虑,能不能实现一个注解,只是生成对应的字段和方法(毕竟最麻烦的就是要复制一堆的属性),而build方法由我们自己来实现,类似下面的代码publicclassA{privateStringa;publicA(Stringa){this。aa;}BuilderWith(A。class)publicstaticclassBuilder{注解自动生成a属性和a(Stringa)方法publicAbuild(){if(anull){thrownewIllegalArgumentException(aisnull);}returnnewA(a);}}}复制代码
  这样的话,我们不仅不用手动处理大量的属性,还可以在build之前加入额外的逻辑,不至于像Lombok的Builder那么不灵活
  然后在后面实现的过程中就发现:
  可以把一个类的属性复制过来,那也可以把一个类的方法复制过来!
  可以复制一个类,那也可以复制多个类!
  于是就发展成了现在这样,给人一种多继承的错觉
  所以说这种方式也会存在很多限制和冲突,比如相同名称但不同类型的字段,相同名称相同入参但不同返回值的方法,或是调用了super的方法等等,毕竟只是一个缝合怪
  这也许就是Java不支持多继承的主要原因,不然要校验要注意的地方就太多了,一不小心就会有歧义,出问题
  目前我主要能想到两种使用场景Builder
  Builder本来就是我最初的目的,所以肯定要想着法儿的实现publicclassA{privateStringa;publicA(Stringa){this。aa;}InheritField(sourcesA。class,flagsInheritFlag。BUILDER)publicstaticclassBuilder{注解自动生成a属性和a(Stringa)方法publicAbuild(){if(anull){thrownewIllegalArgumentException(aisnull);}returnnewA(a);}}}复制代码
  这个用法和之前设想的没有太大区别,就是对应的注解有点不太一样
  InheritField可以用来复制属性,然后flagsInheritFlag。BUILDER表示同时生成属性对应的方法参数组合
  另一种场景就是用来组合参数
  比如我们现在有两个实体A和BDatapublicclassA{privateStringa1;privateStringa2;。。。privateStringa20;}DatapublicclassB{privateStringb1;privateStringb2;。。。privateStringb30;}复制代码
  之前遇到过一些类似的场景,有一些比较老的项目,要加参数但是不能改参数的结构
  一般情况下,如果要一个入参接收所有的参数我们会这样写DatapublicclassParamsextendsB{privateStringa1;privateStringa2;。。。privateStringa20;}复制代码
  新写一个类继承属性多的B,然后把A的属性复制过去
  但是如果修改了A就要同时修改这个新的类
  如果用我们的这个就是这样的InheritField(sources{A。class,B。class},flags{InheritFlag。GETTER,InheritFlag。SETTER})publicclassParams{}复制代码
  不需要手动复制了,A和B如果有修改也会自动同步
  当然这个功能也是很只因肋了,因为我想不出还有其他的用法了,哭手把手教你实现
  要实现这个功能需要分别实现对应的注解处理器和IDEA插件
  注解处理器用于在编译的时候根据注解生成对应的代码
  IDEA插件用于在标记注解后能够有对应的提示AnnotationProcessor
  我们先来实现注解处理器publicclassInheritProcessorextendsAbstractProcessor{Overridepublicbooleanprocess(Setlt;?extendsTypeElementannotations,RoundEnvironmentroundEnv){自定义的处理流程}OverridepublicSetStringgetSupportedAnnotationTypes(){需要扫描的注解的全限定名returnnewHashSet();}}复制代码
  首先我们要继承javax。annotation。processing。AbstractProcessor这个类
  其中getSupportedAnnotationTypes方法中返回需要扫描的注解的全限定名
  然后就可以在process方法中添加自己的逻辑了,第一个参数Setlt;?extendsTypeElementannotations就是扫描到的注解
  我们先拿到这些注解标注的类publicclassInheritProcessorextendsAbstractProcessor{Overridepublicbooleanprocess(Setlt;?extendsTypeElementannotations,RoundEnvironmentroundEnv){for(TypeElementannotation:annotations){获得标注了注解的类Setlt;?extendsElementtargetClassElementsroundEnv。getElementsAnnotatedWith(annotation);}}}复制代码
  通过第二个参数RoundEnvironment的方法getElementsAnnotatedWith就能获得标注了注解的类
  接着我们来获得这些类的语法树,获得这些类的语法树之后,我们就可以通过语法树来修改这个类了JavacElementselementUtils(JavacElements)processingEnv。getElementUtils();JCTree。JCClassDecltargetClassDef(JCTree。JCClassDecl)elementUtils。getTree(targetClassElement);复制代码
  processingEnv是AbstractProcessor中自带的,直接用就行了,通过processingEnv可以获得JavacElements对象
  再通过JavacElements就可以获得类的语法树JCTree。JCClassDecl
  为了后面更好区分,我们把这些标注了注解的类叫做【目标类】,把注解上标记的类叫做【来源类】,我们要将【来源类】中的字段和方法复制到【目标类】中
  我们只要拿到【来源类】的语法树,就可以获得对应的字段和方法然后添加到【目标类】的语法树中
  先通过【目标类】获得类上的注解然后筛选出我们需要的注解,这里我的注解因为支持了Repeatable,所以还要多解析一步获得类上所有的注解Listlt;?extendsAnnotationMirrorannotationstargetClassElement。getAnnotationMirrors();解析Repeatable获得实际的注解Listchildren(List)annotation。getElementValues()。values();复制代码
  拿到注解之后,就可以获得注解上的属性privateMapString,ObjectgetAttributes(AnnotationMirrorannotation){MapString,ObjectattributesnewLinkedHashMap();for(Map。Entrylt;?extendsExecutableElement,?extendsAnnotationValueentry:annotation。getElementValues()。entrySet()){Symbol。MethodSymbolkey(Symbol。MethodSymbol)entry。getKey();attributes。put(key。getQualifiedName()。toString(),entry。getValue()。getValue());}returnattributes;}复制代码
  通过方法getElementValues就可以获得注解方法和返回值的键值对,其中键为方法,所以直接强转Symbol。MethodSymbol就行了
  而对应的值是特定了类型
  值的类型
  值的类
  类
  Attribute。Class
  字符串
  Attribute。Constant
  枚举
  Attribute。Enum
  还有一些我没有用到所以这里就没有列出来了
  所以我们拿到的值有的时候不能直接用,比如我们现在要获得【来源类】的语法树Attribute。ClassattributeClass。。。Type。ClassTypesourceClass(Type。ClassType)attribute。getValue();JCTree。JCClassDeclsourceClassDef(JCTree。JCClassDecl)elementUtils。getTree(sourceClass。asElement());复制代码
  通过上述的方式我们就可以拿到注解上的【来源类】的语法树
  接着我们就可以把【来源类】上的字段和方法复制到【目标类】了for(JCTreesourceDef:sourceClassDef。defs){如果是非静态的字段if(InheritUtils。isNonStaticVariable(sourceDef)){JCTree。JCVariableDeclsourceVarDef(JCTree。JCVariableDecl)sourceDef;Class中未定义if(!InheritUtils。isFieldDefined(targetClassDef,sourceVarDef)){添加字段targetClassDef。defstargetClassDef。defs。append(sourceVarDef);}}方法类似,这里不具体展示了}复制代码
  通过【来源类】语法树的defs属性就能获得所有的字段和方法,筛选出我们需要的字段和方法之后再通过【目标类】语法树的defs属性的append方法添加就行了
  这样我们就把一个类的字段和方法复制到了另一个类上
  最后一步,我们需要在resourcesMETAINFservices下添加一个javax。annotation。processing。Processor的文件,并在文件中添加我们实现类的全限定类名
  这一步也可以使用下面的方式自动生成compileOnlycom。google。auto。service:autoservice:1。0。1annotationProcessorcom。google。auto。service:autoservice:1。0。1复制代码AutoService(Processor。class)publicclassInheritProcessorextendsAbstractProcessor{}复制代码
  引入autoservice包后,在我们实现的InheritProcessor上标注AutoService(Processor。class)注解就会在编译的时候自动生成对应的文件
  到此我们的注解处理器就开发完成了
  我们只需要用compileOnly和annotationProcessor引入我们的包就可以啦IntellijPlugin
  虽然我们实现了注解处理器,但是IDEA上是不会有提示的,这就需要另外开发IDEA的插件来实现对应的功能了
  推荐一下大佬写的小册《IntelliJIDE插件开发指南》,能够比较系统的了解IDEA的插件开发
  这是我的推广链接,如果大家真的要买的,那就顺手点我的推广链接买吧,嘿嘿
  所以项目搭建之类的我就不啰嗦了
  IDEA提供了很多接口用于扩展,这里我们要用到的就是PsiAugmentProvider这个接口publicclassInheritPsiAugmentProviderextendsPsiAugmentProvider{OverrideprotectedNotNullPsiextendsPsiElementListPsigetAugments(NotNullPsiElementelement,NotNullClassPsitype){returnnewArrayList();}}复制代码
  getAugments方法就是用于获得额外的元素
  其中第一个参数PsiElementelement就是扩展的主体,以我们当前需要实现的功能来说,如果这个参数是类并且类上标注了我们指定的注解,那么我们就需要进行处理
  第二个参数是需要的类型,以我们当前需要实现的功能来说,如果这个类型是字段或方法,那么我们就需要进行处理
  直接看代码会清晰一点publicclassInheritPsiAugmentProviderextendsPsiAugmentProvider{OverrideprotectedNotNullPsiextendsPsiElementListPsigetAugments(NotNullPsiElementelement,NotNullClassPsitype){只处理类if(elementinstanceofPsiClass){if(type。isAssignableFrom(PsiField。class)){如果标记了注解,则返回额外的字段}if(type。isAssignableFrom(PsiMethod。class)){如果标记了注解,则返回额外的方法}}returnnewArrayList();}}复制代码
  也就是说扩展的字段和方法是分开来获取的,另外需要注意是额外的字段和方法,不是全部的字段和方法
  接下来我们需要先获得类上的注解privateCollectionPsiAnnotationfindAnnotations(PsiClasstargetClass){CollectionPsiAnnotationannotationsnewArrayList();for(PsiAnnotationannotation:targetClass。getAnnotations()){if(注解的全限定名。contains(annotation。getQualifiedName())){annotations。add(annotation);}if(Repeatable注解的全限定名。contains(annotation。getQualifiedName())){handleRepeatableAnnotation(annotation,annotations);}}returnannotations;}获得Repeatable中的实际注解privatevoidhandleRepeatableAnnotation(PsiAnnotationannotation,CollectionPsiAnnotationannotations){PsiAnnotationMemberValuevalueannotation。findAttributeValue(value);if(value!null){PsiElement〔〕childrenvalue。getChildren();for(PsiElementchild:children){if(childinstanceofPsiAnnotation){annotations。add((PsiAnnotation)child);}}}}复制代码
  获得注解之后,我们就可以通过注解获得注解的属性了CollectionPsiTypesourcesfindTypes(annotation。findAttributeValue(sources));privateCollectionPsiTypefindTypes(PsiElementelement){CollectionPsiTypetypesnewHashSet();findTypes0(element,types);returntypes;}privatevoidfindTypes0(PsiElementelement,CollectionPsiTypetypes){if(elementnull){return;}if(elementinstanceofPsiTypeElement){PsiTypetype((PsiTypeElement)element)。getType();types。add(type);}for(PsiElementchild:element。getChildren()){findTypes0(child,types);}}复制代码
  这里需要注意,Psi是文件树而不是语法树
  比如这样的写法InheritClass(sources{A。class,B。class})
  我们通过findAttributeValue(sources)得到的是{A。class,B。class},最上层是{},{}的子节点才是A。class,B。class,所以Psi体现的是文件的结构
  接着我们就可以获得对应的字段和方法了PsiClasssourceClassPsiUtil。resolveClassInType(PsiType);获得所有字段publicstaticCollectionPsiFieldcollectClassFieldsIntern(NotNullPsiClasspsiClass){if(psiClassinstanceofPsiExtensibleClass){returnnewArrayList(((PsiExtensibleClass)psiClass)。getOwnFields());}else{returnfilterPsiElements(psiClass,PsiField。class);}}获得所有方法publicstaticCollectionPsiMethodcollectClassMethodsIntern(NotNullPsiClasspsiClass){if(psiClassinstanceofPsiExtensibleClass){returnnewArrayList(((PsiExtensibleClass)psiClass)。getOwnMethods());}else{returnfilterPsiElements(psiClass,PsiMethod。class);}}privatestaticTextendsPsiElementCollectionTfilterPsiElements(NotNullPsiClasspsiClass,NotNullClassTdesiredClass){returnArrays。stream(psiClass。getChildren())。filter(desiredClass::isInstance)。map(desiredClass::cast)。collect(Collectors。toList());}复制代码
  上面这几个方法我都是从Lombok里面复制过来的,至于else分支我也看不懂,可能会有多种情况,我也没遇到过,hhh
  然后我们就可以对字段和方法进行复制啦StringfieldNamefield。getName();LightFieldBuilderfieldBuildernewLightFieldBuilder(manager,fieldName,field。getType());访问限定fieldBuilder。setModifierList(newLightModifierList(field));初始化fieldBuilder。setInitializer(field。getInitializer());所属的ClassfieldBuilder。setContainingClass(targetClass);是否DeprecatedfieldBuilder。setIsDeprecated(field。isDeprecated());注释fieldBuilder。setDocComment(field。getDocComment());导航fieldBuilder。setNavigationElement(field);复制代码LightMethodBuildermethodBuildernewLightMethodBuilder(manager,JavaLanguage。INSTANCE,method。getName(),method。getParameterList(),method。getModifierList(),method。getThrowsList(),method。getTypeParameterList());返回值methodBuilder。setMethodReturnType(method。getReturnType());所属的ClassmethodBuilder。setContainingClass(targetClass);导航methodBuilder。setNavigationElement(method);复制代码
  这里大家一定要新实例化所有的字段和方法,不要直接返回【来源类】的字段和方法,因为【来源类】的字段和方法是和【来源类】关联的,而我们返回的是【目标类】的字段和方法,两者不匹配会导致IDEA直接报错
  最后我们只需要在plugin。xml中添加这个扩展就行了extensionsdefaultExtensionNscom。intellijlang。psiAugmentProviderimplementationxxx。xxx。xxx。InheritPsiAugmentProviderextensions复制代码
  最后的最后,需要发布一下插件或是本地集成结束
  附一张插件图
  如果大家有兴趣可以看看这个库,还有很多其他的功能
  链接:https:juejin。cnpost7202272345834094652

想在西安买房,手头有80万做首付,怎么选择比较好?感谢邀请!我先请问题主一个问题。你本人的工作是程序员,如果有网友提这样一个问题公司为了有效的拓展市场,提升公司竞争力,现需要优化提升公司内的某管理系统软件,请问我是该找在行业内有知敢公开你的房贷月供吗?16年勇敢地把一套全款房变成三套贷款房,房贷7000多元,再加上一套公积金月供三千的房,家庭压力大的时候我17年出去上了一年班,目前有两套房都出租了扺了大部分房贷,还是觉得当时的决重庆的车油耗是不是比一般城市的车高很多?这样跟你说吧,在外地我的仪表盘是调成显示平均油耗,在重庆我都是调成剩余里程首先说结论重庆的汽车比起其他差不多经济体量的城市,汽车的平均油耗是要高一些的。比如和成都天津比。如果是同一2021年榆林房价为何居高不下,助推剂是什么?首先煤价上涨导致榆林市的经济增速飞快,2021年gdp预计突破5000亿,人们手里有钱,没有合适的投资渠道,只能投资房产,助推房价上涨。其次,榆林近年来人口数量一直处于净流入状态,生二胎企业奖励6万,三胎9万!鼓励生育政策频出,什么方法才能最有效?原创。生二胎最有效的方法是媳妇愿意把二宝交给我们60几岁的公婆带,公婆退休工资约5000元足够带好宝宝,大米粥稀饭,三岁前奶粉照样把二宝宝带的白白胖胖健健康康,幼儿园上个大班即可,要不要在农村老家建房子?要不要在老家建房子,得看自己的实际情况。像题主所说的这种情况。自己已经在城里有了房子。并且有两个孩子,孩子的培养教育需要一大笔钱,回农村盖房子,又是一笔不小的开支。会为自己带来较大达州房价三至五年会降吗?达州,位于四川省的东北部地级市。辖4县2区1市,人口700万,2017年GDP1580亿。达州是川气(天然气)东输的起点,是川陕鄂渝交汇处的区域城市,房价近些年上涨较快,特别是达州四大会计师事务所看重学历还是CPA证书?四大会计师事务所招聘时更看重学历。四大会计师事务所以校园招聘为主,他们更看重学历,刚毕业的大学生,拿到CPA证书的可能性太少。1本科以上学历。这是最基本的要求,而且看重学校,目前非山东的经济在全国排名怎么样?山东经济是全国第三经济大省十年前山东还是全国第二经济大省,但是2008年互联网经济腾飞,借长三角之便,江苏的衍生经济飞速发展,于是山东被江苏一举反超,并且再也没有回到第二的位置。不为什么比亚迪汽车技术那么好,而销量却不好?销量不好很正常!首先它是个国产自主品牌!现在的国人比较崇拜合资,或者进口品牌。现在市场竟争这么厉害,严重,就是合资车销量也不如从前了!要说技术手平好,恐怕就更没有谁能认可了!就算是惠州汽车站的士车怎么样?你让我说好话还是坏话呢?说坏话吧我跟出租车司机无冤无仇,说好话吧,估计又被人一顿喷,其实我也是左右为难!但是,作为一个在惠州生活了八年之久的外地人来说,滴滴司机给我的印象就俩次,一
台海军演重获国际主动权,佩洛西搬起石头砸自己脚针对美国佩洛西串访台湾,中国人民解放军东部战区在台海开展史无前例的大规模联合实弹军演。长期以来美国不断联合世界各国妖魔化中国,对中国极限施压,利用疫情,俄乌冲突,科技封锁等国际压力封锁台湾的军演比胡锡进的建议更高明之处胡锡进建议伴飞拦截甚至击落佩洛西都是逞一时之快,若擦枪走火,极易引发中美直接对抗,变成中美直接过早摊牌,反而忽略了统一台湾的初终。如果要摊牌犯不上在美快速衰弱前,只要再给点时间,美华春莹七国集团的列强梦该醒醒了外交部发言人办公室消息,在8月4日外交部例行记者会上,总台央视记者提问3日,七国集团外长和欧盟高级代表发表联合声明称,对中方宣布的威胁行动感到关切,可能导致不必要的升级,没有理由利城市早财经茅台存价值好多万亿基酒佩洛西丈夫酒驾被捕后体内被检出毒品国内新闻王毅佩洛西窜访中国台湾地区是一场闹剧,犯我中华者必将受到惩处当地时间8月3日,中国国务委员兼外长王毅在柬埔寨出席东盟外长会时表示,美国国会众议长佩洛西窜访中国台湾地区是一场久加诺夫世界满怀希望注视着中国俄罗斯自由媒体网7月31日刊登采访俄共中央委员会主席久加诺夫的报道,久加诺夫在采访中指出,世界满怀希望注视着中国。报道称,7月28日,中共中央对外联络部以视频方式举办中国共产党与世杜星霖和大31岁张纪中结婚5年,连生两胎,她的婚姻真的幸福吗人们常说娱乐圈是个大染缸,什么样的人都有,打着艺术的和爱的幌子,很多事情都变得名正言顺起来。著名导演张纪中大家肯定非常熟悉了,他执导的神雕侠侣射雕英雄传西游记等不仅捧红了一众明星,捷报丨潇影联合出品奇迹笨小孩斩获百花奖最佳导演最佳新人两项大奖7月30日晚,第36届大众电影百花奖正式揭晓,由潇湘电影集团有限公司联合出品的电影奇迹笨小孩荣获最佳导演(文牧野)最佳新人(陈哈琳)两项大奖。百花竞艳,笨小孩力创奇迹电影奇迹笨小孩色情网站创始人亨特,被曝了还如此猖狂如今,信息安全问题愈发困扰着人们。尤其是女性的私密影像,遭到各种利用。光是今年,鱼叔就已经看到多起案件。都是女孩在分手后,被前男友散播私密视频照片。如此渣男行径,令人发指。这还只是摩托罗拉Edge30Neo现身GeekBench安卓128GB内存IT之家8月4日消息,摩托罗拉Edge30Neo现在已经出现在GeekBench基准测试中,单核成绩664分,多核成绩1848分。根据跑分库中的其他信息,摩托罗拉Edge30Neo毛主席生前要火葬,为何死后却被放进水晶棺?真实原因让人佩服主席已经去世多年了。用了什么方法使他的尸体仍然栩栩如生,保存完好?面对人们的质疑,曾为毛主席遗体保护工作的重要人物韩伯平早已给出了答案。韩伯平说,毛主席的遗体能完好保存至今,不仅仅欧阳夏丹主持新闻联播9年,两年前突然消失,背后有何隐情2011年8月,央视每晚7点和观众不见不散的新闻联播,出现了一张年轻漂亮的新面孔。只见她一头干净利落的短发,播报新闻时落落大方,业务能力丝毫不输曾经的前辈们。这位女孩叫欧阳夏丹,曾
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网