python kotlin_用Java和Python模仿Kotlin构建器

python kotlin

介绍

Kotlin可能现在是我最喜欢的语言,可能它提供的最酷的功能之一是基于几个功能构建的类型安全的生成器(稍后解释)。 我发现自己真的很想在其他两种主要语言(Java和Python)中使用此功能。 本文解释了我认为与使用这些语言的类型安全的构建器最接近的东西。

Kotlin

首先,我需要说明Kotlin进行类型安全的构建器的能力。 要快速了解这些构建器的含义,您应该查看有关它们的页面 。 在本文中,我们将实现其html构建器的一小部分。

Kotlin创建类型安全的构建器的能力归功于许多小功能。 第一个是lambda语法; {param, list -> block.of.code()} 。 如果lambda的参数为零,则可以忽略参数列表和箭头。 当它只有一个参数时,也是如此,因为该参数被隐式称为it 。 例如, {doSomethingWith(it)}是合法的lambda,假设doSomethingWith()接受的对象与传递给lambda的对象的类型相同。

下一个功能是如何将lambda传递给函数。 如果最后一个参数是lambda,则可以在函数调用的括号传递该参数。 例如, myFunc(arg1){lambdaArg()} 。 如果lambda是唯一的参数,则可以完全忽略括号: aFunc{lambdaArg()} 。 这使您可以定义看起来像语言功能的功能。 如果不是因为保留了这些关键字,就可以从技术上定义自己的if-else块或任何循环。

接下来是扩展方法,您可以定义像它们一样工作的lambda。 扩展方法是为接口类之外的类或接口定义的新方法。 例如,您可以为String类创建新的方法。 实际上,它们只是静态方法,它们采用其所针对类型的隐式第一个参数。 在Kotlin代码中,第一个参数分配给this标识符, this标识符隐式使用,就像在实际方法中一样。

您也可以定义像扩展方法一样工作的lambda( SomeClass.() -> Unit而不是(SomeClass) -> Unit ),以便在lambda内部,您可以调用对象而无需显式引用它。

所有这些功能,再加上非常好的类型推断,共同构成了一种功能,可以从使用扩展lambda的函数中创建类型安全的构建器。 因此,我们可以这样写:

html {head {title("A Title")}body {p = "paragraph"p = "'nother one"p = "last paragraph"}
}

要返回Html包含对象HeadBody ,将Head包含Title与文字,“A标题”。 Body包含3个Paragraphs

您可能会注意到title和[p]的定义方式不同。 它可能会是聪明有title使用=语法代替p ,但p展示了这些建设者如何素材可以是优于title 。 我用Python做过类似的事情,因为它也支持属性。

我们来看一下允许创建这些对象的Kotlin代码

fun html(htmlBuilder: Html.() -> Unit): Html {val html = Html()html.htmlBuilder()return html
}class Html {private var head: Head? = nullprivate var body: Body? = nullfun head(headBuilder: Head.() -> Unit) {head = Head()head?.headBuilder()}fun body(bodyBuilder: Body.() -> Unit) {body = Body()body?.bodyBuilder()}
}

我们从Html类和用于启动构建器的html()函数开始。 html函数不是必需的,因为该代码可以用作Html构造函数,但它使我们能够使构造函数保持简单且所有函数都小写而不违反命名约定。

您会注意到,实际上一切都还很短。 只有html函数是3行,这仅是因为它必须在最后返回结果。 如果我们在Html上使用构造函数,那么它将只有htmlBuilder()

这里的HeadTitle

class Head {private var title: Title? = nullfun title(text: String) {title = Title(text)}
}class Title (private val text: String) { }

仍然进行得很好。 Title不需要构建器,因为它仅包含文本。 如果不是因为需要一些更复杂的构建机制,我实际上会让Head只握住String本身,而不是创建Title类和对象。

class Body {private val paragraphs: ArrayList<Paragraph> = ArrayList()var p: Stringprivate get() = null!!set(value) {paragraphs.add(Paragraph(value))}
}class Paragraph (private val text: String) { }

这是真正有趣的事情。 与使用Title方法不同,我们没有使用p()方法,而是使用p的setter继续将Paragraph对象添加到列表中。 在这种情况下,它不是最直观的。 它只是在这里向您展示如何利用这些构建者来发挥创造力。

还要记住,这些类只是构建器类,因此允许它们是有状态的。 应该有一个build()方法,该方法递归调用所有封闭对象的build()方法,以创建一个不错的,不变的对象。

Java

在Java中,您几乎可以创建完全相同的类,除了生成器看起来不那么干净之外,因为它没有上面的所有可爱功能。 因此,首先开始,这是构建器代码最终的外观。

html(html -> {html.head(head ->head.title("A Title"));ht.body(body -> {body.p("paragraph");body.p("'nother one");body.p("last paragraph");});
});

是接近的建设者语法,你可以在Java中获得。 请注意, title()p()的调用方式没有什么不同,因为Java不提供任何类似于属性的构造。 另外,请注意,您需要为所有内容起一个名字。 使用隐式this ,您必须编写类似于hd.title(...)而不仅仅是title(...) ,这甚至没有提到我们必须为lambda定义参数列表这一事实。

您还可以做其他几件事,但更糟糕的是,第一件事就是使用常规代码:

Html html = new Html();Head head = html.head();head.title("A Title");Body body = html.body();body.p("paragraph");body.p("'nother one");body.p("last paragraph");

这并不可怕 ,但是由于缺乏完整的类型推断(我必须指定headbody属于它们各自的类型),它最终变得相对冗长(由于没有括号,所以额外的制表符纯粹是为了外观)用过的。 我想到的另一种方式将在Python版本之后显示,因为它试图复制版本。

因此,让我们看一下代码:

public class Html {public static Html html(Consumer<Html> htmlBuilder){Html html = new Html();htmlBuilder.accept(html);return html;}private Head head = null;private Body body = null;public void head(Consumer<Head> headBuilder) {head = new Head();headBuilder.accept(head);}public void body(Consumer<Body> bodyBuilder) {body = new Body();bodyBuilder.accept(body);}
}

这与直接移植到Java一样直接。 html()函数已作为静态方法移入Html类,因为它必须放在 Java中。 我们使用了Consumer<Html> ,因为这是Java与我们想要的lambda最为接近的东西。

这里是HeadTitle

public class Head { private Title title = null;public void title(String text) {title = new Title(text);}
}public class Title {private final String text;public Title(String text) {this.text = text;}
}

这里没有太多注意事项。 这可能与您的期望有关。 现在以“ Body Paragraph结束。

public class Body {private final List paragraphs = new ArrayList<>();public void p(String text) {paragraphs.add(new Paragraph(text));}
}public class Paragraph {private final String text;public Paragraph(String text) {this.text = text;}
}

几乎感觉像不值得编写这些类,不是吗,它们是如此简单。 请记住,这是准构建器部分。 同样,此代码实际上并不包含用于构建实际的,不变的DOM树的功能。

这就是构建Java版本所需要的。 除了一些语法上的冗长之外,用Java创建比使用Kotlin几乎容易得多,因为没有任何其他功能可以考虑和应用:P

Python

试图找出一种在Python中执行类似操作的方法,这使我很幸运地看到了一个视频,该视频展示了使用上下文管理器( with语句)的新颖(但不直观)的方法。 Python中的问题在于,lambda只允许具有单个表达式或语句。 上下文管理器通过有效地允许您在可以在上下文管理器中使用的条目上返回一个对象(或不返回任何对象),从而允许(非常有限的)方式避免单行lambda,就像在lambda中一样。

因此,例如,构建器在Python中如下所示:

myhtml = Html()
with myhtml as html:with html.head() as head:head.title("A Title")with html.body() as body:body.p = "paragraph"body.p = "'nother one"body.p = "last paragraph"

这实际上看起来像是浪费,因为这几乎可以很容易地写成以下内容:

html = Html()
head = html.head()
head.title("A Title")
body = html.body()
body.p = "paragraph"
body.p = "'nother one"
body.p = "last paragraph"

with块的最大好处是缩进,因为Python具有缩进限制,这是因为Python在花括号上使用了缩进。 为此上下文管理器可能值得。 但是,在向您展示了用Python制作这些代码所需的基本代码之后,我还将在书中谈到另一个好处:

class Html:def __enter__(self):return selfdef __exit__(self, exc_type, exc_val, exc_tb):return Falsedef head(self):self._head = Head()return self._headdef body(self):self._body = Body()return self._body

在这里,你可以看到Html类具有所需的__enter__()__exit__()方法是一个上下文管理器。 他们几乎什么也不做; __enter__()仅返回self ,而__exit__()仅表示它__exit__()任何可能传入的异常__exit__() head()body()方法几乎可以满足您现在的期望, HeadBody也是上下文管理器类型的假设。

class Head:def __enter__(self):return selfdef __exit__(self, exc_type, exc_val, exc_tb):return Falsedef title(self, text):self._title = Title(text)class Title:def __init__(self, text):self.text = textclass Body:p = property()def __enter__(self):return selfdef __exit__(self, exc_type, exc_val, exc_tb):return False@p.setterdef p(self, text):if not hasattr(self, 'paragraphs'):self.paragraphs = []self.paragraphs.append(Paragraph(text))class Paragraph:def __init__(self, text):self.text = text

唯一值得关注的新事物是对Bodyp标签使用了property 。 幸运的是,我们并不需要对干将property ,我们需要有返回小号None ,就像在Kotlin。

好的,现在我们来看一个有趣的,不太明显的原因,在这种情况下使用上下文管理器会有所帮助。 在Java和Kotlin中,我们最终需要对build()方法进行一次额外的调用(或者让html()函数替我们完成),并让它最后一次进行一次递归遍历照顾它。 使用上下文管理器, __enter__() __exit__() __enter__()__exit__()方法可以在进入时传递对象的生成器版本,然后在退出时进行构建。 这意味着构建器的每个中间阶段在退出时已经包含完整构建的版本。

实际上,将头缠绕起来可能有点困难。 这是一个使用HtmlHtmlBuilderHead进行部分实现的示例:

class Html:def __enter__(self):self._builder = HtmlBuilder()return self._builderdef __exit__(self, exc_type, exc_val, exc_tb):self.head = self._builder._headself.body = self._builder._bodydel self._builderreturn Falseclass HtmlBuilder:def head(self):self._head = Head()return self._headdef body(self):...class Head:def __enter__(self):self._builder = HeadBuilder()return self._builderdef __exit__(self, exc_type, exc_val, exc_tb):self.title = self._builder._titledel self._builderreturn False

在这里, Html对象的__enter__()方法创建并保存一个生成器,然后将其返回。 在__exit__() ,它从存储在构建器中的值构建自身,并从自身中删除构建器。 一经考虑,至少对于我而言,可能会认为存储在构建器上的对象不是完成的对象,而是完成的对象。 生成器对象上的方法将返回具有其自己的__exit__() __enter__()__exit__()方法的适当类,这也将确保HtmlBuilder正确构建,如HtmlBuilderhead()方法和Head的实现所示。 使用此设置,调用代码实际上仍然与第一次相同。

最后一件事:既然我们知道可以使用上下文管理器来执行此操作,则您可能会认为Java的try资源管理器实际上可以正常工作。 而且你会是对的。 实际上,它的最终语法(除了随机try关键字之外)也比lambda版本更干净。 调用时,资源管理器版本如下所示:

Html html = Html();
try(html) {try(Head head = html.head()) {head.title("A Title");}try(Body body = html.body()) {body.p("paragraph");body.p("'nother one");body.p("last paragraph");}
}

在这一点上,我将留给您尝试并弄清楚如何实现。 提示:我认为它不能像Python构建的第二个版本那样工作,它会随其构建。 我认为此Java版本的代码中的所有内容都需要构建器,直到最后,您在html上调用build()方法来创建真实版本。

奥托罗

天哪,这个东西最终有点长,不是吗? 我希望您能从此练习中获得一些乐趣,因为我不确定它的实际用途(除了了解您可以使用上下文管理器模拟0或1参数lambda外,还可以。

可悲的是,我从来没有像在Kotlin网站的示例中那样谈论添加其他参数,例如在函数调用中分配类,id等。 Kotlin还具有其他功能,可以使它真正干净和容易,但是本文显然没有足够的空间。 下周我会解决。

谢谢阅读!

注意:截至昨天,所有编辑已完成。 从这里开始,我“只是”需要设计一个我有想法的封面; 获取所有印刷版和电子书版本的格式; 编写附录(大部分只是本书中的代码片段的集合,充实了更多内容); 并完成GitHub存储库的编写,该库将具有所有超级有用的类和函数,可更快,更轻松且更少地构建自己的描述符。 我希望所有这些工作都能在夏季结束之前完成,但希望能早日完成。 我的生活将变得更加忙碌,所以我不知道我能花多少时间来完成所有这些工作。

翻译自: https://www.javacodegeeks.com/2016/01/mimicking-kotlin-builders-java-python.html

python kotlin

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

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

相关文章

android 4.4.2截屏方法,android4.4.2 使用 uiautoviewer 截屏报错

1、正常启动后&#xff0c;使用 uiautomatorviewer 没有问题2、由于要使用 uiautomator&#xff0c;每次启动 uiautomator 服务后再使用 uiautomatorviewer 就会图片错误&#xff0c;启动 uiautomator 服务的步骤如下&#xff1a;1、Download jar files from uiautomator jsonr…

java fastutil_具有FastUtil的精简Java集合

java fastutil针对我最近在GNU Trove库上发表的《 发现Java原始资源集合的处理 》一书 &#xff0c; TheAlchemist指出了fastutil优于trove的一些优点&#xff1a;“我更喜欢fastutil&#xff08; http://fastutil.di.unimi.it/ &#xff09;&#xff0c;因为它仍在积极开发中&…

android 圆角按钮渐变,Android实现圆形渐变加载进度条

最近设计要求要一个圆形进度条渐变的需求&#xff1a;1.画圆形进度条2.解决渐变最终实现效果代码package com.view;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.grap…

javaone_JavaOne 2015:为JDK 9做准备– blog @ CodeFX

javaoneJavaOne 2015看到了Project Jigsaw团队关于Java 9中的模块化的一系列讨论 。它们都是非常有趣的&#xff0c;并且充满了宝贵的信息&#xff0c;我敦促每个Java开发人员都注意它们。 除此之外&#xff0c;我想给社区一种搜索和引用它们的方法&#xff0c;因此我在这里总…

android wifi 通讯录,通过wifi和gmail从symbian手机中将名片夹(通讯录)导入到android手机 | 古意人...

使用背景与条件&#xff1a;最近入手了android手机defy&#xff0c;机器到手后兴奋过度&#xff0c;马上将原symbian手机诺基亚E50的sim卡和内存卡都转移到了defy上&#xff0c;捣鼓了一段时间的defy&#xff0c;突然想起要同步原诺基亚手机的名片夹到defy中(android中貌似称为…

oracle aq_通过Java 8流使用Oracle AQ

oracle aqOracle数据库最令人敬畏的功能之一是Oracle AQ&#xff1a;Oracle数据库高级队列 。 AQ API直接在数据库中实现了完整的事务性消息传递系统。 在数据库处于系统中心的经典体系结构中&#xff0c;使用AQ进行进程间通信时&#xff0c;多个应用程序&#xff08;其中一些…

c语言建立多个有名管道,命名管道一个非常经典的题目

#include /*特别注意写管道时&#xff0c;设置打开管道文件的格式必须为可写*/#define FIFO_SERVER "myfifo"#define OPENMODE (O_WRONLY | O_NONBLOCK)int main(int argc, char **argv){int fd;int nwrite;/*打开管道文件&#xff0c;可写非阻塞*/if ((fd open(FIF…

jit 方法内联_JIT编译器,内联和转义分析

jit 方法内联即时&#xff08;JIT&#xff09; 即时&#xff08;JIT&#xff09;编译器是Java虚拟机的大脑。 JVM中对JIT编译器的影响最大。 一会儿&#xff0c;让我们退后一步&#xff0c;看看已编译和未编译语言的示例。 诸如Go&#xff0c;C和C 之类的语言之所以称为编译语…

nodejs android 推送,利用Nodejs怎么实现一个微信小程序消息推送功能

利用Nodejs怎么实现一个微信小程序消息推送功能发布时间&#xff1a;2021-01-20 13:55:29来源&#xff1a;亿速云阅读&#xff1a;92作者&#xff1a;Leah今天就跟大家聊聊有关利用Nodejs怎么实现一个微信小程序消息推送功能&#xff0c;可能很多人都不太了解&#xff0c;为了让…

kafka spark_您在2016年会做什么? Apache Spark,Kafka,Drill等

kafka spark让我们玩得开心。 这是新的一年的开始-我们正处于新事物的门槛上-因此让我们期待您在2016年可能会做的事情。现在我知道做出预测的风险&#xff0c;尤其是有记录的预测&#xff0c;但是我很高兴您能在一年后回访&#xff0c;看看我对2016年的预测是如何完成的。 您…

android ini文件格式,ini是什么格式的文件?ini文件怎么操作?

类型&#xff1a;Android平台大小&#xff1a;622KB语言&#xff1a;中文 评分&#xff1a;5.0标签&#xff1a;立即下载ini文件主要存放用户所做的选择以及系统的各种参数。用户可以通过修改INI文件,来改变应用程序和系统的很多配置。自定义一个文件&#xff0c;会成一个deskt…

android开发 apk文件,android开发中,将数据库文件与APK一起发布?

2015-11-11 回答可以将xx.db文件复制到eclipse android工程中的res\raw目录中。所有在res\raw目录中的文件不会被压缩&#xff0c;这样可以直接提取该目录中的文件。使用opendatabase方法来打开数据库文件&#xff0c;如果该文件不存在&#xff0c;系统会自动创建/sdcard/dicti…

jpa 关联实体的关联实体_JPA实体锁定模式的差异

jpa 关联实体的关联实体JPA本质上提供了两种锁定机制&#xff0c;以帮助同步对实体的访问。 两种机制都可以防止以下情况&#xff1a;两个事务在不知道的情况下相互覆盖数据。 通过实体锁定&#xff0c;我们通常希望通过2个并行事务来防止以下情况&#xff1a; 亚当的事务读取…

华为p6电信版 android 4.5,华为P6电信版系统应用apk补全教程 完整EMUI

现在要说的是华为P6电信版的系统应用apk补全教程&#xff0c;给你一个非阉割版的完整EMUI系统。大家应该都知道&#xff0c;电信定制版由于华为系统服务与电信的定制APP功能重叠&#xff0c;语音助手、云同步、云端备份、手机找回等系统应用apk全被“阉割”掉了&#xff0c;这几…

黑马ee在职进阶视频_进阶– Java EE 7前端5强

黑马ee在职进阶视频系列继续。 在初步概述和Arjan关于最重要的后端功能的文章之后 &#xff0c;我现在非常高兴让Ed Burns&#xff08; edburns &#xff09;使用他最喜欢的Java EE 7前端功能完成本系列。 感谢Markus Eisele让我有机会在他非常受欢迎的博客上发表帖子。 我和M…

如何构建股票ChatGPT查询全球股票市场以及常用Prompt

Blog&#xff1a;4 ways to use ChatGPT Stock Chatbot for stock analysis of Global Stock Markets NASDAQ NYSE LSE HKEX TSE NSE HANGHAI SHENZHEN 地址&#xff1a;http://deepnlp.org/blog/chatgpt-stock-global-market 全球股票市场&#xff1a;NASDAQ 纳斯达克, NYSE…

web.xml.jsf_看一下即将发布的JSF 2.3 Push支持

web.xml.jsf如前几篇文章所述&#xff0c;下一版本的JavaServer Faces&#xff08;Mojarra&#xff09;已添加了许多增强功能。 JSF 2.3计划于2017年与Java EE 8一起发布&#xff0c;但是您现在可以通过从源代码构建或运行里程碑版本来获得JSF的一些增强功能和更新以用于测试目…

android视频教程那个讲的最好,最全的android视频教程推荐 android视频教程怎么学习效果好...

很多同学对android学习很感兴趣&#xff0c;都对此有这样的疑问&#xff1a;“最全的android视频教程推荐&#xff0c;android视频教程怎么学习效果好?”那这里android培训专家就给我们来具体讲解下。Android开发学习主要是学习Android平台下开发的基础知识以及项目编程的实用…

android size_t在哪个头文件,size_t

size_t 类型定义在cstddef头文件中&#xff0c;该文件是C标准库的头文件stddef.h的C版。它是一个与机器相关的unsigned类型&#xff0c;其大小足以保证存储内存中对象的大小。例如&#xff1a;bitset的size操作返回bitset对象中二进制位中1的个数&#xff0c;返回值类型是size_…

jqgrid mvc_将JQGrid与Spring MVC和Gson集成

jqgrid mvc我在一个单页面应用程序上工作&#xff0c;我想在使用Spring MVC的应用程序的一部分中使用网格功能。 自从我上次使用JQGrid以来已经有一段时间了&#xff0c;找到让我起床所需的信息有点困难。 在这篇文章中&#xff0c;我想整理所有信息并将其放入教程中&#xff0…