“很少有人真正在Kotlin上编写后端”-帕夏·芬克尔斯坦(Pasha Finkelstein)的访谈

如何从无望中成长为成功的程序员? 今天,在我们的虚拟工作室Pasha中, asm0dey Finkelstein回答了问题。 Pasha是少数知道如何在Kotlin上创建后端的人之一。 此外,他看到了开源,积极参与了社区的生活,并且一分钟之内,他参加了我们几乎所有的Java Java会议。


如何找时间提交


-有几个主题可以讨论。 首先,您将在Joker上发表演讲。 其次,您是社区的活跃成员,不断地做某事。 第三,您经常参加我们的会议,出于某种原因,您认为这很好。


-关于我是社区成员的事实。 我和大多数人一样,都患有冒名顶替综合症,我不觉得自己在做很多有用的事情,尤其是对于Java社区,尤其是最近。 我做的最后一件事对社区很有用-伙计们和我为Spring写了一个很酷的库,叫做spring-flow-state-machine ,它允许您控制应用程序中对象的状态。 她又小又舒服。


-这是正在主要Spring上推广的图书馆吗?


-不,可能是Spring Statemachine在那里,它非常悲惨:它编写得很差,工作得很奇怪,而且绝对不起作用。 当您的应用程序是某种有限状态机并且在不同状态下工作方式不同时,Spring Statemachine会管理应用程序的状态。 我们做了另一件事:当您拥有某种实体时,您可以为其设置生命周期,并可以驱动它贯穿此生命周期。 我们的任务是考虑您的交易如何运作以及所有这些。 如您所知,我们仅将@Transactional批注放在一个正确的位置,这足以让我们说它控制状态。 最主要的是,有一个方便的DSL,它使您可以说出在哪里可以做的以及沿途需要做的事情。


“而且我正确地理解,既然这个东西控制着抽象的东西,那么你可以在它上面做Spring Statemachine所做的同样的事情吗?”


-是的,是的,有细微差别。 这个东西控制的主要实体似乎是具有id或类似名称的实体。 这实际上是一个非常小的库,包含两个接口,两个exceptionov和四个类。


“而Spring所做的事情–为什么您认为这很奇怪,为什么不清楚为什么呢?”


-为什么需要它是可以理解的,只是做得很差。 据说它知道如何做。 首先,没有收集它们在文档中提供的示例;其次,如果您尝试深入研究源代码(并且在想要做某事时,总会深入研究Spring的源代码,因为并非所有内容都被编写了)在文档中),您会发现它的编写方式最好不要编写。 顺便说一句,似乎Spring Batch通常也有同样的抱怨。


“您也使用Spring Batch吗?”


-在我看来,我几乎使用了Spring生态系统中的所有内容。


“而且您将如何继续使用它?”


-您知道,在下一个工作地点,我将没有Spring,而我将拥有HER。 这不是我的选择。 我可能会用过Spring,但是实际上,在我们的播客中,我曾经说过,现在我最喜欢的是Jooby 框架,它了解一切。 它是由某个Java冠军由一个人编写的。 还有一个依赖注入,它是基于Guice构建的。 很酷,它拥有一个生态系统,顺便说一下,它不同于所有其他微框架。


-我看到有两种选择-Java和Kotlin。


“我怀疑你什么都可以。” Scala可能也是可能的,但是您必须放弃Guice,它可能不适用于Scala。 和往常一样,我喜欢带注解的构造函数注入。 这是在Guice中完成的。


-而且,如果可以使用GraalVM静态组装这样的应用程序,那么它将只是空间。


“我认为您不会在那里赢得太多。” 就像春天一样-当然,您会在性能方面赢得一些。


-例如,提高启动速度。


-当然,在生产中,我们将拖动一位Java冠军编写的少量内容,我们将为那里的启动速度而战!


我知道了


-可能真正关心启动速度的人可能会使用Azul Zing,并使用在裸机SE上编写的内容对其进行调整。


“我是否正确理解您编写的库是开源的?”


-是的,它是我前任雇主的开源软件,某种程度上属于他,因为它在工作草案中使用过。


我知道了 也就是说,您是在工作时间写的。


-是的,他们在上班时间写信。


-您认为,下班后有编写软件的前景吗?


-我几个小时后才写软件,尽管现在它不是真正的软件。 我写的最后一件事……现在在俄罗斯,锁的问题是现实的,对我而言,紧迫的问题是绕过这些锁。 有一个很棒的代理服务器3proxy ,所以我为Fedora,Ubuntu,Debian和Centos编写了Ansible剧本 ,对其进行了全面测试,并在GitHub上进行了共享。 在Ansible Galaxy中,也可以共享。


“那你在哪里找到时间?”


-我像每个人一样,都在时间上遇到问题,因为我在工作中累了,有一个家庭,我需要至少与家人在一起。 但是在周末,一家人睡得很晚,我是个百灵鸟,很早就醒了,早上我的头很好,我可以做很多事情。 有时我什至在上班之前就写点东西。


-对于那些想开始用开放源代码编写东西但行不通的人,您有什么建议?


-问问自己为什么不起作用。 根据问题的答案,尝试写点东西。


-有一个标准答案:没有时间,不清楚我在哪里需要它。


-至于“没有时间”,碰巧确实没有时间,但是确实没有时间。 “没有时间”是我没有动力的一种表达方式。 这是绝对清楚的,而不是必须战斗的事实。 并非所有人都必须致力于开源。 这是为自己和社区做某事的一种很酷的方法,但是没有人对任何人有任何欠款。


如果不清楚谁需要您,那么问题又是为什么。 原因有两个:您在编程中仍然很少有时间做类似的事情,并且您似乎觉得这很无聊,然后可能是对的,在这个阶段您无法帮助任何人。 尽管我认识一些人,例如Slava Semushin,这是他做的第一件事,但他开始在生产环境中进行编程之前就开始走私于Alt Linux 。 这是他的编程方式(当然这是个玩笑)。 但是他真的什么都不懂,一开始他就了解。 这不再是一个玩笑。


碰巧的是,人们从事企业很长一段时间,却不了解他们能做什么。 我在这种情况下已经很长时间了。 大约在4年前。 我已经在企业工作了6年,不知道该怎么做。 但是随着时间的流逝,我发现我想重写的系统的这一组件原则上可以分离为一个单独的模块,并且过分敏感。 必须征得雇主同意。 最主要的是迈出第一步。


有时雇主不想协调,这是一个非常生动的话题。 例如,Tinkoff并不急于开放其某些后端组件,尽管从理论上讲是可以的。


随着时间的流逝,您开始看到机会。 最主要的是将其隔离为一个单独的组件。 如果雇主不允许,您将在雇主内部使用它,就像在Zalando一样,将有内部开源。 然后在下一个项目中,您将看到这东西也可以被区分。


例如,或再次编写。


-关于“再写一次”。 我们最后解决的问题之一是日志记录。 显然,所有krupnyaki都与集中式日志记录相混淆,根本没有这种生命。 对此的标准解决方案是ELK堆栈,ElasticSearch,Logstash,Kibana。 仅有一个小问题-这是Logstash。 ElasticSearch,Kibana运作良好,但Logstash无效。 它要么无法持久运行,要么运行非常缓慢。 如果它不能持续工作,则其队列大小为5000条消息。 塞满了更多邮件-邮件开始丢失。 坦率地说,这是令人不快的功能。 因此,我们有卡夫卡代替他。 在Sberbank中,我们编写了自己的Kafka logback附加器 ,该附加器也是开源的,并且本身运行良好。


-那就是那个人!


-已经有三个了,我不确定你对我们的看法。


好的 返回到开源和日志记录主题。 如果人们使用Linux以及祖父发明的方式,为什么人们经常在企业中使用集中式记录器,尝试自己编写它们,例如将所有内容都上传到文件中?


-我不知道Linux中的常规集中式日志记录。 有一个rsyslog,这是一个很好的格式,但是要说得通一点,例如不适合Java,因为如果您查看某种stackrace,那么突然发现stackrace是很多行,而不仅仅是一行。 因此,在rsyslog中,这些将是单独的日志条目。 如果所有这些都混杂在一起,在我看来,这将伤害所有人。


-是的。


-老实说,我不知道rsyslog能提供什么保证。 当我有某种解决方案时,我通常会陷入困境,尝试在CAP定理中找到它的位置。 而且我只是没有在rsyslog上找到此信息,也许我看上去很糟糕。 但是很快我无法确定。 他通常保证交付给我们,并且保证交付多长时间? 他嘶嘶声在某个地方时会有什么时间戳记? 如果他保证,这已经很好了,如果您通常使用各种跨度和痕迹进行记录,那么无论他在那做什么,您都仍然会弄清楚。 如果不能保证交货,那绝对是一场灾难。 从这个意义上讲,我对kafka的信任度更高,但它具有更多的带宽。 老实说,尽管我不太了解卡夫卡。


-在那里,您可能需要以某种方式将日志分为关键/非关键日志,因为如果日志服务器崩溃,您将永远不知道发生了什么。


当我们谈论Java时,一切都非常简单。 我们在某些Docker中运行,并且在ERROR级别输入了stdout日志。 从调试或从轨道开始,一切都开始流入Kafka。 我是那种通常不会关闭借记记录的人。 我从没活过看到如此美好的时刻,当我意识到这就是全部时,我不再使用日志记录来分析问题。


“例如,如果所有这些TB级的日志都飞到了那里,您是否不担心Kafka网络的吞吐量会随即消失?”


-我们总是太小,每天有100 GB的日志,还有100 GB-这个卷是多少? 主要问题是如何存储它,而不是如何传输它。 您是否真的有TB级的日志?


-我不知道磁盘上的存储量是多少,但是我知道有时系统启动太慢,以至于我们没有等待它启动。 有人不小心进行了调试或跟踪,仅此而已。


-等一下 在我看来,还是通过网络同步日志时您走错了路?


-不,他们刚倒入卡夫卡。


-但是同步吗? 您不太可能选择异步执行此操作,即使您完全固执并且不希望对日志进行保证,也必须至少从一个节点上等待来自Kafka的ACK。 您至少还等待一个ACK。


-可能是。


-我这么说是因为我们编写了logback附加程序,所以我理解了我们的指导。 但是,我们将此追加程序包装在AsyncAppender中,然后通过实验计算出我们在峰值处有多少消息,并且此AsyncAppender中的缓冲区扭曲得更多,例如峰值增加了10%。 原来,我们的应用程序没有被阻止。


“你有什么可怕的故事。” 为什么这不是Java中的标准,为什么要动手写呢?


-由于您必须用所有语言进行手写,因此仅使用Java至少可以清楚地写在哪里。 而且,如果您有任何好处,我也不知道从哪里开始。


-所以您认为没有可以添加到Maven的库,并且所有内容都将自行安装?


-让我们从他们没有普通的Maven的事实开始,因为他们有Ninja,Make,CMake,qmake,而Maven的一切都有些沉重。 当然,它们具有用于记录的库,它们都是Java语言,并且很清楚如何将附加程序附加到它们。 此外,在Java中,它们美观,灵活,通常与外墙等配合使用。 如果您看一看Rust,就会发现有些完全可悲的东西。 我不知道在哪里放置和写下记录器。 我几乎不掌握如何配置它们。


“那么您真的喜欢Java吗?”


-我喜欢生态系统。 我喜欢Kotlin是一种语言,而Java是一种生态系统。 顺便说一句,没有一种语言比Kotlin更好,我现在一点也不知道。 一旦卡在Groovy上,我什至会得到一件Baruch的T恤,上面刻有Groovy。



为什么选择科特林?


-在Java,Scala,Groovy和Kotlin之间进行选择-为什么要选择Kotlin?


-良好语法和复杂性的完美结合,使自己陷入困境。 我不喜欢岩石,因为它很容易用腿拍,有时不适合自己,但不适合自己的邻居。 您添加隐式变量,一切对您而言都是神奇的,地狱在他的调试中。 当然,您可以同意不使用它们,然后您会失去岩石的某些魅力。 当然,如果您完全聪明,则可以在Rock中使用宏,除非您了解其工作原理,否则没有人可以使用。


-在Rock中完成宏,结果是?


-我想说它们起作用,但是我不想说它们可以使用。 而且,即使您更聪明,也可以使用Cats之类的库。 但是,当您变得更聪明一些并且已经对使用scalaz和使用Cats感到无聊时,这可能处于同一水平。 但是除了您之外,没有人可以阅读此代码。


-和其他Cat支持者。


“您说这是真的,但事实并非如此,因为其他Cats拥护者使用它的方式有所不同。”


-这是该系统功能的一部分。


-非常强大,让人联想到BFG


-脚下射击。


“有时候对自己,有时对其他人来说,是多么幸运。” 顺便说一句,我注意到在岩石上写作的人有时会忘记思考。 他们具有如此漂亮的概念,可以在不同的位置连续插入两次,而不必考虑所有这些都需要以某种方式包装在事务中的事实。 但是他们有一些巧妙的方式(或者现在在Scala中使用SQL流行的任何方式)对数据库的漂亮调用。


-但是您可以在Java中的两个地方执行此操作...


-但是在Java中,我有Spring,并放置了@Transactional批注。 在Java中,通常不做复杂的事情。 顺便说一下,在科特林,似乎也是如此。 但是在岩石接受了。 对于Groove,这当然是一种很酷的语言,除非您单方面进入字节码或遇到一个事实,即您不了解所拥有的对象类型。 他们有时还喜欢大喊任何有效的Java代码就是任何有效的凹槽代码,但这不是真的,因为匿名内部类在那里不起作用。 您不能接受和实例化。 您必须创建一个lambda,然后进行转换。 因此,我选择Kotlin,它的语法与Java中的语法略有不同,但是如果您确实愿意的话,那里有一个转换器。 另一方面,没有新的复杂性。 没有什么地方不能按住ctrl键单击并不能继续。 按住ctrl键单击双重等于,然后切换到equals方法。


-如此湿滑的时刻:您信任JetBrains吗? 现在,许多人并不真正相信Java,因为它是Oracle,但是使用JetBrains怎么办? 普通的人坐在那里,在发展一种语言吗?


-我不想破坏报告的一部分,但总的来说,我喜欢他们所说的话,并且我喜欢我所看到的。 我无法选择“不信任”。 好吧,好吧,我不信任JetBrains,然后一切都变得非常可悲,因为我们由一些相对理智的人开发了哪些其他语言? Golang大概是Rust(由Mozilla开发),由Google开发。 另外,它们都是由社区开发的,甚至包括Java。 尚不清楚您可以信任谁以及为什么可以信任。 我的妄想症有问题,我不知道该怎么做。


在Joker会议的第一天(2018年10月19日至20日),Pasha将在报告“ Kotlin-生产2年,而不是一个空白”中谈论Kotlin的后端。 小丑是我们的主要会议之一,我们不断在Habré上进行介绍。 如果可能的话,一定要来。

-最近,Baruch和我发现了有关Kotlin主题的某种研究,该研究表明Kotlin上带有40种鹦鹉(学习型鹦鹉)的程序比Java上的程序更好。


-更好,但是总之,如果我的记忆正确的话。


-两者都更短和更好。 他们用代码气味来衡量所有这些。 我不知道怎么说 从某种意义上说,可能可以用“更好”一词代替。


太好了 只是知道,在两种语言的任何比较中,都有一个小问题。 他们是否实现了相同的任务,并且使用了相同的测量工具? 代码粗体在Java和Kotlin中是否具有相同的质量,这是真的吗? 还是他们用Java编写了最近20年并学会了如何找到800对夫妻的代码,而在Kotlin中他们编写了2年并学会了找到20对夫妇?


-哇,这是一个很好的问题。 但是,有趣的不是他们得出的数字,而是他们要处理的问题。 他们只有5个人,我也想问你。 该地区任何地方的Kotlin适应水平如何? 这取决于您与多少人一起工作,有多少个项目,有多少个示例。


-很明显,所有Android都几乎已经迁移到Kotlin(现代开发),并且后端的迁移非常缓慢。 我的报告的目标之一就是加快这一过程。 当我来到HH接受采访时说,过去两年我一直在Kotlin编写后端,他们告诉我,可能吗? 这仅适用于手机! 雇主真的不知道这是可能的。 员工可能也不知道。 很难说出自适应的程度,我认为在Android中,自适应率是80-90%,而在后端,最大值是10%。 是的,十点! 如果算上所有旧系统,则不会有百分之二。 但是,如果仅计算新项目,则可能占10%。


-也就是说,至少已经有后端Kotlin这样的东西存在了吗?


-是的,我是在Sberbank的房地产中心写的。 在此之前,他还写道。


-好 简而言之,许多人说这是JetBrains的营销方式,在世人士对此的反应会有所不同。


-我的报告的一部分致力于以下事实:因此,在我们的项目成功运行之后,公司的整个Java部分开始转向Kotlin。


-因此,下一个问题进一步出现:应用程序在Kotlin上的代码百分比是多少? 首先,您可以将Java和Kotlin作为基础。 其次,Kotlin可以用作DSL并开始在其上连续重写所有内容。


-从我的角度来看,很少有需要故意干扰Java和Kotlin的情况。 在Kotlin上编写整个项目很有意义。 也许如果您觉得要将整个应用程序转换为Kotlin,可以先开始在Kotlin中编写新的类,然后在某个时候开始将旧的类从Java转换为Kotlin并用笔进行修剪。 但是我必须说,“ Java2Kotlin”是一个一般的工具,并不完美。


-“ Java2Kotlin”是指文件之间的Idea中的Ctrl C-Ctrl V?


-是的,但是可能仍然有些捷径。


-顺便说一句,我曾经尝试让Scala在Idea中完全使用Ctrl C-Ctrl V,但是结果却很少见。


“我也不喜欢Java2Kotlin的功能。” 我写得更好。 当然,有时您可以从这里开始。 您有两种方法:要么转换整个项目(尤其是在项目不是很大的情况下),然后更正所有错误的位置。 或者您按照一种经典的方式来做:长时间手写,需要很多机械工作。 但是您可能想从某个地方开始。


-是否有某种复杂性与哑巴语法无关,而与语义部分有关?


-看来我会在报告中谈到这一点。


-好 将Kotlin添加到项目后,其数量会进一步增加还是减少?Kotlin应该多少钱才能开始吞噬一切?


“我没有这种经验。” 除了一种情况,我们立即在Kotlin中编写了该应用程序。您是否受Idea插件生态系统的引导?


-一点点。


— , , , - . - , , , , , .


— Community Edition , , .


— Community Edition , Ultimate.


— Ultimate , Spring-.


— , , Spring-, .


— -, , endpoints, , .


— . endpoint', ?


— , . , , .


— , , Shift-Shift-Shift, - , Shift-Shift-Shift , , endpoint' , . , - .


— , Search Anywhere, .


-是的 , , . . , , , . , , , , , , , , .


— , ?


— , . , , , .


— . , , , . . , , , , .


— , , , , , , , , , . . , -. . , , . Java-, . generic , , invariants, in. . . , , . , . , Haskell, ? : map, fmap, .


— .


— : , , . , — sequence. — , .


, , , - . GraalVM , , . , @graalvm_ru.


— ?


— . 2, 2 2 . .


— , . , .


— - . : for- , for-, ( baseline), . 2 6 . , — : sequence' sequence'. , , - . . . , . , , , baseline.


— , ?


— , - .


— , , GraalVM .


— . JDK10 JVMCI , GraalVM EE, , C2.


— - , Community Edition -Community Edition, , .


— . , , . . , , , .


— ! :-)


— , .



« »


— . : . , . : ?


— . . « ». , , , , : , , , . 2008 , , , . , . , , . .


— ?


— 4 C++ Builder, Java, , -, , . Java , C++ Builder, , , C++ Builder, . . , . , , , .


— — VCL, .


— , . , - , - . - , SQL, . , , .


— C++.


— , Java, . C++ - , , , , , . MVC. , NetBeans, , . , , . GroupLayout BorderLayout. , , , , . , , -.


— , IDE-?


— . , , - Electron.


— . , Java.


— , . - - : JavaScript. .


— GraalVM , Electron JavaScript -.


— . , , , . Nashorn . Truffle- JavaScript? , .


— , , Nashorn, . Electron, .


— , ?


— , Electron.


— ?


— Node.js, Graal Node.js, . .


— ?


— . - .


— . — Node.js, Electron Builder, SQLite ORM . , , — SQLite — 10 . -, , . .


— , , , .


— . . , Java FX, , , , 10- . , JRE, . , — , . , -. , - , .


— , .


— - , - , . -, .


— - desktop environment , , -. , - to do list, - . , , , , . , , , . — F1, .


— , , . - – CLI-. , Task Warrior, . , .


— « ». , , , - . ?


— , ?


— .


— , .


— -. , ?


— , : «, , ?» : «». , - . - , - , . : « ?» , . : «, . , ?» «». « , ».


— : , , . ? , - ?


— , , , : . (5 , ), , - , , , , . , , , - , , , , .


— , , — , , ? , , ?


— , - . , . , . . , . . .


— : - , , - — SQL , . .


“他们好吗?” 唯一重要的问题是它们是否健康。没有一种适合所有人的抽象正确生活方式。对我来说,前进并寻找有趣的任务是正确的。对于某人来说,坐着不动,做永远可以做的工作并为此获得报酬是正确的。需要所有人。


-顺便说一句! 您知道很多有趣的事情,您可以说出来。 您为什么每年不参加我们的会议并提供一些新报告?


“我认为我不了解很多。”


“但是你还是来了!”


-偶然地,我在这个行业中拥有或多或少的独特专业知识。 很少有人真正在Kotlin上编写后端。 似乎他们都不愿意谈论它。 他们可能没有这种传福音的精神。 我一直渴望教别人如何生活(或至少尝试)。 因此,我当然很感兴趣。 而且,我对这份报告有些兴趣,因为我要来找下一位在科特林写信的下一位雇主会说:“为什么?”,我要说的是,我现在在Joker做了一份报告。让我们看看。


-当您了解自己有足够的专业知识可以举报时,该如何表述?


-不行 生成该报告并不是因为您具有专业知识,而是因为您有一定的经验并希望谈论它。 我很想从第一次进入JPoint时就已经知道了。 但是不久之前,我有一个刻意讨论“要告诉”的话题。 这样的话题让您可以谈论45分钟。



为什么要参加会议?


-为什么要召开会议? 为什么需要它们? 从您通常会去找他们的事实来看,事实证明您需要它们。


-不,从我去找他们的事实来看,结果只有我去找了他们。


“但是出于某种原因,您需要它们吗?”


-我不知道我准备如何回答这个问题。 我不确定是否要去参加会议以获得信息。 从某种意义上说,我去参加会议是为了让你认识我。


-这是正常现象。


-当我说“你”时,我的意思是很多记得我的人。 总体而言,对市场来说,仅凭我的价值就可以了解我,这对我很有用,尤其是当我留下良好的印象时。 我尝试这样做,因为我实际上似乎并不了解,而且我一直都缺乏有趣的工作。 我一直在寻找更有趣的工作。 几乎没有什么东西比我现在所看到的更有趣。


另一方面,我去参加一个会议以进入肠道。 任何地方都没有这样的机会。 我去听过Azul Zing的工作原理,因为它很有趣。 是的,我几乎无法使用这些知识,但这很有趣。 这很难在书中读到。 我还有另一个问题:我会读书。 我更喜欢读小说。 我可能是至少有一些像这样生活的认真的技术人员之一。 我读过参考书,白胡椒,但几乎从未看过书。 在参考文献和白胡椒中,这种信息很少是可能的。


-是的,在书籍中这种信息很少发生。


-老实说,书对我来说直接很难。 我在Java上读的最后一本重要著作是“ Java Concurrency in Practice”,在Java生态系统上是“ High-Performance Java Persistence”,Vlad Mihalcea(这是一本很好的书)。 但是通常这很难读,也没有给我。


-阅读Java码头值得吗?


-这取决于您的学习方式。 根据学习方式,人们分为两种类型:有些人喜欢先写作,然后理解其工作方式,有些人则先阅读如何写作,然后再写作。 我属于第一类。 我总是从实验开始,不深入。 我潜水很晚。 一个很棒的女孩和我一起工作,在写Kotlin的第一行之前,她去读了Kotlin上的基座。


-顺便说一句,我可能会做到的。


-这是一个很棒的方法,我对此没有反对。


“我对像你这样的人如何能立即正常书写也很感兴趣。”


-没有要正常写入的任务,有要写入的任务。 这很重要。 通常,编写任务通常不会总是出现,特别是在学习时。 当我学到一些东西时,我不仅会理解一堆信息,然后去写作。 我总是从实验开始。 也许您知道我有时写播客。 在我们记录并出版的第一期中,我谈到了这样一个事实,我之所以能够学习所有内容,仅仅是因为我从不推迟它。 我没有读书的任务,因此节省了很多时间,因为我立即坐下来并开始在工作项目或家庭项目中使用某种技术。 然后,如有必要,我会更深入。 但是对于初学者来说,我可以告诉您我了解如何使用它。 在下一步中,我可以告诉您我知道它是如何工作的。 我在意识到弹簧如何工作之前就开始使用弹簧。 大概是6岁。


-好像我们应该汇总一下:-)非常感谢! 在Joker见,我一定会来听您的报告的。

Source: https://habr.com/ru/post/zh-CN421749/


All Articles