kotlin ++ --_顺便说一句-探索Kotlin代表团

kotlin ++ --

by Adam Arold

亚当·阿罗德(Adam Arold)

顺便说一句-探索Kotlin代表团 (By the way — exploring delegation in Kotlin)

Kotlin has an interesting keyword, by, which can be used for delegation. There is a lot of confusion around it, so in this article we’ll clean that up.

Kotlin有一个有趣的关键字by ,可用于委派。 周围有很多困惑,因此在本文中我们将对其进行清理。

If we check the keyword reference page of Kotlin, by is listed there. It says that it is capable of two different things:

如果我们检查关键字参考页Kotlin的, by被列出。 它说它有两种不同的能力:

by:

by

- delegates the implementation of an interface to another object
-将接口的实现委派给另一个对象
- delegates the implementation of accessors for a property to another object
-将属性访问器的实现委派给另一个对象

I think this is the source of most confusion. After reading this article, you’ll be able to see that these two things are essentially the same in Kotlin.

我认为这是最令人困惑的原因。 阅读本文之后,您将可以看到,这两件事在Kotlin中基本上是相同的。

Before we do that, we should clarify what delegation is in the first place. By perusing the related Wikipedia page we can find the following definition:

在此之前,我们应该首先弄清什么是授权。 通过仔细阅读相关的Wikipedia页面,我们可以找到以下定义:

In software engineering, the delegation pattern is an object-oriented design pattern that allows object composition to achieve the same code reuse as inheritance.

在软件工程中,委托模式是一种面向对象的设计模式,它允许对象组合实现与继承相同的代码重用。

I think this is a pretty accurate description, and it also comes with a Kotlin code example!

我认为这是一个非常准确的描述,并且还附带了Kotlin代码示例!

As we have seen above Kotlin supports delegation of an interface and delegation of accessors for a property. Let’s take a look at how they work in detail.

如前所述,Kotlin支持接口的 委派和属性的访问者的委派 。 让我们看一下它们如何详细工作。

接口的委托 (Delegation of an interface)

For this exercise we’ll use Position:

在本练习中,我们将使用Position

Positionable:

Positionable

and DefaultPositionable which is just an implementation of Positionable:

DefaultPositionable ,这只是Positionable的实现:

The above functionality can be used without the by keyword like this:

可以在没有by关键字的情况下使用上述功能by如下所示:

This involves a lot of boilerplate code as seen in the above example. The purpose of the by keyword is to do away with this kind of drudgery. It allows object composition to achieve the same code reuse as inheritance:

如上例所示,这涉及许多样板代码。 by关键字的目的是消除这种繁琐的工作。 它允许对象组合实现与继承相同的代码重用

So in a nutshell: with by we get Item 16 from Effective Java for free. Pretty nifty, huh?

简而言之:通过这种方式by我们免费获得了来自Effective Java的Item 16 。 很漂亮吧?

看一下引擎盖 (Taking a look under the hood)

Now that we know how it works on the outside, let’s check out the internals. This is really important as we’ll see later. This is the Java code which will be generated from the Rect implementation above:

现在我们知道它在外部的工作原理,让我们检查一下内部结构。 这一点非常重要,我们将在后面看到。 这是将从上面的Rect实现中生成的Java代码:

You can do this yourself in IDEA by using Tools > Kotlin > Show Kotlin Bytecode and then clicking Decompile.

您可以使用自己做的IDEA Tools > Kotlin > Show Kotlin By tecode然后CLI cking Dec Ømpile。

So what Kotlin does is that it takes our delegate, DefaultPositionable, puts it in the resulting class as a private final field named $$delegate_0 and adds implementations for all the methods of Positionable and in them it calls our delegate.

因此Kotlin要做的是,将我们的委托DefaultPositionable放入结果类中,作为名为$$delegate_0private final字段,并添加所有Positionable方法的实现,并在其中调用我们的委托。

If by eyeballing the above code you had guessed that we can have multiple delegates in a class, you’d be right! Let’s do just that by extracting width and height into its own delegate:

如果通过查看上面的代码而您猜到了我们可以在一个类中拥有多个委托,那将是对的! 让我们通过将widthheight提取到自己的委托中来做到这一点:

With Sizable we can have a class which has the same functionality as Rect but with only delegates:

使用Sizable我们可以拥有一个与Rect具有相同功能但只包含委托的类:

The resulting Java code is this:

产生的Java代码是这样的:

This also means that with delegation we can unit test our building blocks in isolation (DefaultPositionable and DefaultSizable here). This is a significant improvement over inheritance in itself.

这也意味着,通过委托,我们可以隔离地对构建基进行单元测试(此处为DefaultPositionable和DefaultSizable)。 这是对继承本身的重大改进。

So it turns out that with the by keyword we have a really powerful alternative to inheritance. The only limitation is that we have to use interfaces on the right-hand side of by:. Kotlin can only delegate to interfaces.

事实证明,使用by关键字,我们有一个非常强大的继承替代方法。 唯一的限制是我们必须在by的右侧使用interface 。 Kotlin只能委托给接口。

This also means that Kotlin supports Mixins or Multiple inheritance.

这也意味着Kotlin支持MixinMultiple继承

Remember that multiple inheritance comes in 3 “flavors” . They are inheritance of state, implementation and type. Java originally only supported multiple inheritance of type (with interfaces). Java 8 added support for multiple inheritance of implementation (with default methods). By using the by keyword we can have multiple inheritance of state as well!

请记住,多重继承有3种“味道”。 它们是状态,实现和类型的继承。 Java最初仅支持类型的多重继承(带有接口)。 Java 8添加了对实现的多重继承的支持(使用默认方法)。 通过使用by关键字,我们还可以具有状态的多重继承!

接口委托的变体 (Variations of interface delegation)

There are many ways of using interface delegation, and not all them work in a way as you would expect. Rect in fact could have been defined with all the examples below to similar effect, but with slight differences in utility:

使用接口委托的方法有很多,但并非所有方法都能按您期望的方式工作。 实际上,可以用下面的所有示例对Rect进行定义,以达到相似的效果,但实用性略有不同:

RectWithConstructor lets us pass custom implementations of Positionable to our Rect. With RectWithProperty we can have access to our delegate within our Rect. With RectWithDefaultValue we can achieve both while being able to just pass a Position to the constructor if we are fine with the default value:

RectWithConstructor允许我们将Positionable自定义实现传递给我们的Rect 。 使用RectWithProperty 我们可以Rect中 访问我们的代表 使用RectWithDefaultValue我们可以在实现默认值的情况下实现这两种功能,同时只需将Position传递给构造函数:

RectWithFunction is a bit of an outlier. It is there to demonstrate that we can also have functions producing delegates for us. This will be useful later when we’ll try to understand property delegation.

RectWithFunction有点离群值。 在那里证明我们也可以具有为我们生成委托的函数。 稍后当我们尝试了解属性委托时,这将很有用。

Note that RectWithDefaultValue achieves multiple inheritance of state as well.

注意, RectWithDefaultValue实现状态的多重继承。

接口委托的陷阱 (The pitfall of interface delegation)

You might have wondered about what happens when we can not only pick an implementation for our delegate but change it at runtime:

您可能想知道,当我们不仅可以为委托选择实现时,还可以在运行时更改它时,会发生什么情况:

This looks good, but if we take a peek at the generated Java code we’ll see a discrepancy:

这看起来不错,但是如果我们看一下生成的Java代码,就会发现差异:

Yep, if we make our delegate a var and change it at runtime, it won’t change the actual delegate. It will happily set the value of the positionable property and leave $$delegate_0 unchanged. This can lead to pretty nasty problems if you forget this, so my advice is not to do this.

是的 ,如果我们将委托设为 var并在运行时进行更改,则不会更改实际的委托。 它将愉快地设置positionable属性的值,并使$$delegate_0保持不变。 如果您忘记了这一点,可能会导致非常麻烦的问题,所以我的建议是不要这样做。

If you try to hack around this by renaming positionable to $$delegate_0 it won’t work — you’ll get a compiler error detailing a JVM name clash.

如果您尝试通过将positionable重命名为$$delegate_0来解决该问题,那么它将无法正常工作-您会收到详细说明JVM名称冲突的编译器错误。

财产委派 (Property delegation)

You might have seen code like this before:

您之前可能已经看过这样的代码:

and wondered about why we have a keyword for seemingly unrelated things. To fully understand how this works we need to take a look at the difference between properties and fields.

并想知道为什么我们有一个看似无关的事物的关键字。 为了完全理解它是如何工作的,我们需要看一下属性字段之间的区别。

属性和字段 (Properties and Fields)

In Java we have fields and they look like this:

在Java中,我们有字段 ,它们看起来像这样:

There are no methods, no strings attached, just our solitary field. If we write the same class in Kotlin, while it might look similar:

没有方法,没有附加条件,只有我们一个人的领域。 如果我们在Kotlin中编写相同的类,则看起来可能类似:

the Java code generated from it will be a bit different:

从它生成的Java代码将有所不同:

So here is the difference: fields are just solitary slots which store data. Properties on the other hand have getters and if we declared them as a var they will also have setters. This difference is very important. We’ll see how this makes it possible to have not only interface delegates but property delegates as well!

所以这是区别字段只是存储数据的单独插槽。 另一方面, 属性具有getter ,如果我们将其声明为var它们还将具有setter 。 这种差异非常重要。 我们将看到这如何使不仅有接口委托而且有属性委托成为可能!

There are cases when the Kotlin compiler won’t even declare a backing field for a property. For example with this Kotlin code:

在某些情况下,Kotlin编译器甚至不会声明属性的后备字段。 例如,使用以下Kotlin代码:

The generated Java code will only contain a method. Since the value of foo can’t be changed and it is pre-determined:

生成的Java代码将只包含一个方法。 由于foo的值无法更改,并且它是预先确定的:

There are more to properties but I won’t explain them here. The Kotlin docs are rather verbose about this topic. You can check them out here.

属性还有更多,但我在这里不再解释。 Kotlin文档对此主题相当冗长。 您可以在此处查看它们。

偷看实际的房地产代表 (Peeking at an actual property delegate)

With this knowledge we have everything to understand how property delegation works. If we take a look at the docs for this topic (which I recommend reading thoroughly) we can see this:

有了这些知识,我们就能了解财产委派的工作原理。 如果我们看一下该主题的文档 (我建议您仔细阅读),我们会看到以下内容:

There are certain common kinds of properties, that, though we can implement them manually every time we need them, would be very nice to implement once and for all, and put into a library. Examples include:

有某些常见的属性,尽管我们可以在需要时手动实现它们,但一劳永逸地实现并将其放入库中会非常好。 示例包括:

- lazy properties: the value gets computed only upon first access;
-惰性:仅在首次访问时才计算值;
- observable properties: listeners get notified about changes to this property;
-可观察的属性:侦听器会收到有关此属性更改的通知;
- storing properties in a map, instead of a separate field for each property.
-将属性存储在地图中,而不是为每个属性存储一个单独的字段。

To cover these (and other) cases, Kotlin supports delegated properties:

为了涵盖这些(和其他)情况,Kotlin支持委托的属性:

Let’s revisit the example above which has a lazy property delegate:

让我们重新看一下上面的示例,它有一个lazy属性委托:

If we take a look at what the lazy function does:

如果我们看一下lazy函数的作用:

we can see that it returns a SynchronizedLazyImpl which is a class that implements Lazy:

我们可以看到它返回了SynchronizedLazyImpl ,它是实现Lazy的类:

Lazy also comes with an extension function:

Lazy还带有扩展功能:

which is there to implement the getValue operator.

在那里可以实现getValue运算符。

You can read more about operator overloading here.

您可以在这里阅读更多有关运算符重载的信息

The main point here is that delegating to a property uses the exact same mechanisms behind the scenes as interface delegation. The differences which cause the confusion are the getValue/setValue operators. We need them to implement within our property delegates. Not because of how delegation works, but because how overriding property access operators work.

这里的重点是,委派给属性使用与幕后委托完全相同的机制。 引起混淆的差异是getValue / setValue运算符。 我们需要它们在我们的财产代表中实施。 不是因为委派工作原理,而是因为最重要的财产访问操作员如何工作

Calling lazy() to delegate to an instance of Lazy is the same on the code level as calling fetchPositionable() in the example above to delegate to an instance of Positionable.

在代码级上,调用lazy()委派给Lazy的实例与在上例中调用fetchPositionable()委派给Positionable的实例相同。

如何写我们自己的代表 (How to write our own delegate)

Kotlin gives us two interfaces to use for this purpose: ReadOnlyProperty and ReadWriteProperty :

Kotlin为我们提供了两个用于此目的的接口: ReadOnlyPropertyReadWriteProperty

This further reinforces the similarity between the two kinds of delegation. Let’s take a look at how we can implement a property which is not initialized during object construction but at a later time, but still won’t let us read null values from it:

这进一步加强了两种授权之间的相似性。 让我们看一下如何实现一个属性,该属性在对象构造过程中未初始化,但在稍后的时间初始化,但是仍然不允许我们从中读取null值:

Note that this class is in the Kotlin stdlib, but it is not well known that it is there.

请注意,此类位于Kotlin stdlib中,但并不知道它在那里。

We can also add a factory method to produce it:

我们还可以添加一个工厂方法来产生它:

and we can use it like this:

我们可以这样使用它:

看一下Java代码 (Taking a look at the Java code)

If we decompile the Java bytecode from SomeClass above, we’ll see this:

如果我们从上面的SomeClass反编译Java字节码,我们将看到:

Looks strangely familiar, no? Kotlin generates a field for our delegate. (someValue$delegate). Kotlin uses it when either the getter or the setter of our property is called.

看起来很陌生,不是吗? Kotlin为我们的代表生成一个字段 。 ( someValue$delegate )。 当调用我们属性的getter或setter时,Kotlin会使用它。

By having properties instead of fields, property delegation can work out of the box. Properties come with getters and setters. Kotlin can provide custom implementations for them when we want to use property delegates.

通过使用属性而不是字段,属性委托可以立即使用。 产品附带吸气剂和吸气剂。 当我们要使用属性委托时,Kotlin可以为其提供自定义实现。

With interface delegation we can have all three kinds of multiple inheritance: state, implementation, and type. It also lets us use composition without the boilerplate.

使用接口委托,我们可以拥有所有三种多重继承:状态,实现和类型。 它还使我们可以使用没有样板的合成。

And also we now understand what the documentation says about delegation. With interface delegation we delegate the implementation of an interface to another object. With property delegation we delegate the implementation of accessors for a property to another object.

现在,我们也了解了文档中有关委托的内容。 使用接口委托,我们 接口 的实现委托给另一个对象。 通过 属性委托,我们 属性访问器 的实现委托给另一个对象

Now, with all this new knowledge under our belt let’s go forth, and Kode on!

现在,有了我们掌握的所有这些新知识,我们继续前进,然后Kode继续前进!

Thanks for reading! You can read more of my articles on my blog.

谢谢阅读! 您可以在我的博客上阅读更多我的文章。

翻译自: https://www.freecodecamp.org/news/by-the-way-exploring-delegation-in-kotlin-cc7281d5b498/

kotlin ++ --

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值