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
包含对象Head
和Body
,将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()
。
这里的Head
及Title
。
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");
这并不可怕 ,但是由于缺乏完整的类型推断(我必须指定head
和body
属于它们各自的类型),它最终变得相对冗长(由于没有括号,所以额外的制表符纯粹是为了外观)用过的。 我想到的另一种方式将在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最为接近的东西。
这里是Head
和Title
:
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()
方法几乎可以满足您现在的期望, Head
和Body
也是上下文管理器类型的假设。
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
唯一值得关注的新事物是对Body
的p
标签使用了property
。 幸运的是,我们并不需要对干将property
,我们需要有返回小号None
,就像在Kotlin。
好的,现在我们来看一个有趣的,不太明显的原因,在这种情况下使用上下文管理器会有所帮助。 在Java和Kotlin中,我们最终需要对build()
方法进行一次额外的调用(或者让html()
函数替我们完成),并让它最后一次进行一次递归遍历照顾它。 使用上下文管理器, __enter__()
__exit__()
__enter__()
和__exit__()
方法可以在进入时传递对象的生成器版本,然后在退出时进行构建。 这意味着构建器的每个中间阶段在退出时已经包含完整构建的版本。
实际上,将头缠绕起来可能有点困难。 这是一个使用Html
, HtmlBuilder
和Head
进行部分实现的示例:
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
正确构建,如HtmlBuilder
的head()
方法和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
python kotlin_用Java和Python模仿Kotlin构建器相关推荐
- 用Java和Python模仿Kotlin构建器
介绍 Kotlin可能现在是我最喜欢的语言,并且它可能提供的最酷的功能之一是基于几个功能构建的类型安全的生成器(稍后解释). 我发现自己真的很想在其他两种主要语言(Java和Python)中使用此功能 ...
- java和python的比较-java 和 python的一些对比
Python比Java简单,学习成本低,开发效率高 Java运行效率高于Python,尤其是纯Python开发的程序,效率极低 Java相关资料多,尤其是中文资料,Python国内的资料大多数情况无法 ...
- java和python的比较-java和python的比较
1. 在实际运用的python入门简略,但要学会用python干活,需求再学习python各种库,pyhton的强壮在于库,为什么python的库强壮,原因是python的库能够用python,c言语 ...
- java和python哪个好学-Java VS Python 应该先学哪个?
http://www.tuicool.com/articles/fqAzqi Java 和 Python 是当下两种巨火的巨强大的编程语言,对于刚开始学习编程的同学来说,很是迷惑,最经常问得问题就是, ...
- java和python哪个好学-Java和Python的前景哪个更好 学习难度呢
2018年学Python发展前景怎么样?零基础如何学习Python? Java开发和Python开发哪个更好学?在过去 15年的时间里,Python一直呈现稳步上升的趋势,终于在前几年进入了 TIOB ...
- python编程语言-为什么Java、Python会成为程序员最害怕的编程语言?
声明:本文来自于微信公众号 InfoQ(ID:infoqchina),作者:Mike Loukides,授权站长之家转载发布. 这是 O'Reilly 发布的"The Least Liked ...
- 你的眼中满是“变量”,可“变量”眼中是无相(Python)(Java与Python学习通法)
(一)Python 中的变量 1. 什么是变量 任何计算机的CPU都不具备存储功能,比如我们用计算机计算 1+ 1 等于几这个问题,1+1 必须是存储形式存在于计算机中,然后计算机的CPU 才能计算: ...
- java构造方法嵌套,laravel查询构建器中的嵌套查询
我需要根据请求ID获得1个帖子,结构如下: postId; postTitle; postContent; postImage; bandName; genreName; 标签:[tagId,tagN ...
- java多个构造方法_Java构建器(多个构造器参数)
今天看netty权威指南,第一次听说构建器,百度了几个博客,但是并没有通俗易懂一点儿的,综合别人的博客,总结如下: 1. 构建器是什么? 当创建对象需要传入多个参数的时候我们通常会根据参数的数量写不同 ...
最新文章
- c# 逆转数组元素的排序
- 20155301《信息安全系统设计基础》第六周学习总结
- 工业机器人的控制方式
- 什么是MARC数据?
- gc java root_深入理解Java中的Garbage Collection
- PHP程序无法设置cookie
- Ubuntu里面vi编辑器在编辑文本时 如何在所有行行首或行尾插入字符
- Unity Animator动画状态机 深入理解(一)
- MATLAB数字图像处理实验题目要求
- 推荐一个磁盘清理工具
- WPS ppt添加幻灯片编号无反应
- PPT设计思维进阶:提升设计能力
- html用ul li制作导航条
- 一张图读懂一个产业短视频第4期
- python窗口界面自适应_自适应页面的实现方式
- word插入公式自动编号 #不起作用的解决方案
- 谱图理论(Spectral and Algebraic Graph Theory)| Chapter1: Introduction
- 亿级经纬度距离计算88.73秒,秒杀VBA!
- Android studio不停indexing解决
- LiveVideoStackCon 2018技术培训 — 从FFmpeg视频编码到抖音式视频特效实现
热门文章
- [2020.11.25NOIP模拟赛]下棋【dp】
- 2021牛客暑期多校训练营7 K-xay loves sequence(主席树+二分)
- codeforces1303 F. Number of Components(并查集+添_正序、删_逆序)
- codeforce23 E. Tree(高精度+树形dp)
- 【斐波那契】【前缀和】无限序列
- 【动态规划】公共子串
- 一分钟理解Java公平锁与非公平锁
- 汇编语言(三十五)之输入字符串以$结束然后输出字母个数
- 汇编语言(二十四)之输出n行星号
- Redis进阶之主从复制