kotlin 内联类_Kotlin:类型别名和内联类

kotlin 内联类

The Kotlin programming language has two distinct features that may be used for similar purposes. Those are inline classes and type aliases. You could use them to improve code clarity and type safety, but what’s the difference between them? This is the topic of this article.

Kotlin编程语言具有两个不同的功能,可用于相似的目的。 这些是内联类类型别名 。 您可以使用它们来提高代码的清晰度和类型安全性,但是它们之间有什么区别? 这是本文的主题。

类型别名 (Type Aliases)

Type aliases first appeared in Kotlin version 1.1. They effectively allow you to declare a new name for any existing type. This declaration doesn’t create a new type, and you can pass the aliased type wherever the alias is expected (something that’s called assignment compatibility):

类型别名最早出现在Kotlin 1.1版中。 它们有效地允许您为任何现有类型声明新名称。 该声明不会创建新的类型,您可以在需要别名的任何地方传递别名类型(这称为赋值兼容性 ):

typealias Str = Stringval s: Str = "Hello"

This example is probably not very useful. Let’s apply type alias to make a complex generic type more readable. Suppose we have two classes, Customer and Division, and we need to implement some processing of customers grouped by division:

这个例子可能不是很有用。 让我们应用类型别名使复杂的泛型类型更具可读性。 假设我们有两个类, CustomerDivision ,并且需要对按部门分组的客户进行一些处理:

data class Customer(val name: String)
data class Division(val name: String)typealias CustomersByDivisionMap = Map<Division, List<Customer>>
class Processor {
fun processCustomers(customers: CustomersByDivisionMap) { customers.forEach { entry ->
println(entry.key)
entry.value.forEach(::println)
} }
}

We could save more characters by aliasing the type Map<Division, List<Customer>> to something even shorter, but I chose to create a more descriptive type name. As you see, we don’t need to cast or extract the value of the type from the alias — inside the method, we use the customers variable just as if it were a Map, which it is.

我们可以通过将Map<Division, List<Customer>>类型别名更短一些来节省更多字符,但是我选择创建一个更具描述性的类型名称。 如您所见,我们不需要强制转换类型或从别名中提取类型的值-在方法内部,我们使用的customers变量就像是Map

So this is how you could call the method that accepts this alias:

因此,这就是您如何调用接受此别名的方法的方法:

Processor().processCustomers(
mapOf(
Division("North")
to listOf(Customer("John"), Customer("Mary")),
Division("South")
to listOf(Customer("Fred"), Customer("Jill"))
)
)

Note that I pass a regular map to this method, although it accepts a type alias.

请注意,我将常规映射传递给此方法,尽管它接受类型别名。

If I now decompile the generatedProcessor.class class to Java (e.g. using thejavap utility), I’ll see the following:

如果现在将生成的Processor.class类反编译为Java(例如,使用javap实用程序),则会看到以下内容:

public final class Processor {  public final void processCustomers(
Map<Division, ? extends List<Customer>>); public Processor();}

So the alias was just replaced with the original type. This hints at an important limitation — the compiler won’t allow you to have a clash of two methods with the same signature, differing only in argument type aliases. E.g. this won’t work:

因此,别名仅被替换为原始类型。 这暗示了一个重要的限制-编译器不允许您将具有相同签名的两个方法冲突,仅在参数类型别名上有所不同。 例如,这将不起作用:

fun processCustomers(customers: CustomersByDivisionMap) { }
fun processCustomers(customers: Map<Division, List<Customer>>) { }

The first method will be replaced with the same signature as the second, which is not allowed in the target Java code.

第一种方法将被与第二种方法相同的签名替换,这在目标Java代码中是不允许的。

内联类型 (Inline Types)

Inline types were first introduced in Kotlin 1.3. Moreover, they are still (as of version 1.3.72) in experimental status. They are essentially overhead-free wrappers around the values of other types. Let’s try to rewrite the previous type alias example using inline classes:

内联类型首先在Kotlin 1.3中引入。 而且,它们仍处于实验状态(从1.3.72版开始)。 它们实际上是围绕其他类型的值的无开销包装器 。 让我们尝试使用内联类重写以前的类型别名示例:

inline class CustomersByDivisionWrapper(
val map: Map<Division, List<Customer>>)class ProcessorInline {
fun processCustomers(customers: CustomersByDivisionWrapper) { customers.map.forEach { entry ->
println(entry.key)
entry.value.forEach(::println)
} }
}

And this is how you could call this method:

这就是调用该方法的方式:

ProcessorInline().processCustomers(
CustomersByDivisionWrapper(mapOf(
Division("North")
to listOf(Customer("John"), Customer("Mary")),
Division("South")
to listOf(Customer("Fred"), Customer("Jill"))
)
))

Note three immediately visible differences from type aliases:

请注意与类型别名的三个立即可见的区别:

  • the inline class not an alias, but a wrapper with a single property, representing the wrapped value;

    内联类不是别名,而是具有单个属性的包装器,表示包装的值;
  • to access the wrapped value, you use its property name (in our case, map)

    要访问包装的值,请使用其属性名(在我们的示例中为map )

  • to pass a wrapped object to the method accepting a wrapper, you have to instantiate the wrapper using its constructor; simply passing the value to the accepting method won’t work.

    要将包装的对象传递给接受包装的方法,您必须使用其构造函数实例化包装; 仅仅将值传递给接受方法是行不通的。

Now let’s look at the compiled ProcessorInline class:

现在,让我们看一下已编译的ProcessorInline类:

public final class ProcessorInline {  public final void processCustomers-RBqTKbU(
Map<Division, ? extends List<? extends Customer>>); public ProcessorInline();}

Note that the type of the method arguments didn’t change — the method still accepts a Map, so there’s no wrapping overhead involved. However, note the method name — processCustomers-RBqTKbU. This is the so-called method mangling — any method accepting the inline class receives a unique name.

请注意,方法参数的类型没有改变-该方法仍然接受Map ,因此不涉及任何包装开销。 但是,请注意方法名称processCustomers-RBqTKbU 。 这就是所谓的方法处理 -接受内联类的任何方法都会收到一个唯一的名称。

If we now add a method that accepts the original type (or any other inline class wrapping the same type, e.g. AnotherWrapper), there won’t be a compiler error, e.g. this works:

如果我们现在添加一个接受原始类型的方法(或包装相同类型的任何其他内联类,例如AnotherWrapper ),则不会出现编译器错误,例如,可以这样做:

fun processCustomers(customers: CustomersByDivisionWrapper) { }fun processCustomers(customers: AnotherWrapper) { }fun processCustomers(customers: Map<Division, List<Customer>>) { }

Such method overloading is possible specifically thanks to the method name mangling. The decompiled code will now look like the following:

特别是由于方法名称修饰,可以实现这种方法重载。 现在,反编译的代码将如下所示:

public final class ProcessorInline {  public final void processCustomers-sjwEFNk(
Map<Division, ? extends List<? extends Customer>>); public final void processCustomers-bme9n3A(
Map<Division, ? extends List<? extends Customer>>); public final void processCustomers(
Map<Division, ? extends List<Customer>>); public ProcessorInline();
}

It’s worth saying, however, that in some cases the value still gets wrapped. You could have noticed that the CustomersByDivisionWrapper.class file was generated on compilation — specifically for this purpose.

值得一提的是,在某些情况下,价值仍然会被包裹。 您可能已经注意到, CustomersByDivisionWrapper.class文件是在编译时生成的-专门用于此目的。

结语 (Wrap-Up)

Inline classes and type aliases provide somewhat similar features (they may be used as an alternative for a certain type while avoiding the overhead of creating a wrapper at runtime.

内联类和类型别名提供了一些相似的功能(它们可以用作特定类型的替代方法,同时避免在运行时创建包装器的开销。

Type aliases may be substituted by the original types, but their declaration doesn’t create a new type. One of the consequences is that you could run into the clashing of methods that accept type aliases and original types.

类型别名可以用原始类型代替,但是它们的声明不会创建新的类型。 结果之一是,您可能会遇到接受类型别名和原始类型的方法的冲突。

Inline classes are not assignment-compatible with the wrapped values, but their declaration involves the creation of a new type and mangling of methods that accept those types, meaning the method name clashing won’t occur.

内联类与包装的值在赋值上不兼容,但是内联类的声明涉及创建新类型和接受接受这些类型的方法的处理,这意味着不会发生方法名称冲突。

翻译自: https://medium.com/swlh/kotlin-type-aliases-and-inline-classes-dd1b42e24bfb

kotlin 内联类

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值