重学 Kotlin —— typealias ,换了个马甲,我就不认识你了 ?

简介: 重学 Kotlin —— typealias ,换了个马甲,我就不认识你了 ?

今天的主角是 type alias,翻译过来叫 类型别名。先来看一下文章目录:

  1. 什么是 typealias ?
  2. typealias 的本质
  3. typealias 存在的意义是什么?
  4. typealias 的使用注意事项


什么是 typealias ?



这是一个很基础的关键字,但可能很多人没有使用过。它的作用十分简单,给已有类型取一个别名,可以像使用原类型一样使用这个 “类型别名”


举个简单的例子:

typealias Name = String
val name : Name = "java"
println(name.length)
复制代码


String 取个别名 Name ,在使用过程中,NameString 是完全等价的。

既然是等价的,使用别名的意义是什么呢?

别急,typealias 不仅仅支持给类取别名,它的用法丰富的让你想象不到。


// 类和接口
typealias Name = String
typealias Data = Serializable
// 函数类型
typealias GetName = () -> String
typealias Handler = CoroutineScope.() -> Unit
// 泛型
typealias P<T> = Comparable<T>
typealias Pairs<K, V> = HashMap<K, V>
typealias Numbers = Array<Number>
// object
object Single {}
typealias X = Single
class Util {
    companion object {
        val count = 1
    }
}
typealias Count = Util.Companion
// inner class
typealias NotificationBuilder = NotificationCompat.Builder
class Outer { inner class Inner }
typealias In = Outer.Inner
// 枚举
enum class Color { RED, YELLOW, GREEN }
typealias color = Color
// 注解
typealias Jvm = JvmStatic
复制代码


上面的枚举用法中,需要注意的一点是,只能为枚举类 Color 取别名,无法为具体的枚举值取别名 。诸如 typealias Red = Color.RED 是不允许的。


几乎没有 typealias 不适用的类型。说到现在,你又得疑问了,类型别名存在的意义是什么 ?这样简单的取个别名,为什么不直接使用原类型呢 ?


typealias 的本质



暂且别急,我们先来看一下 typealias 的实现原理,说不定可以有一些发现。

反编译下面这个简单的例子:

typealias Binary = ByteArray
fun getBinary(string: String) : Binary = string.toByteArray()
复制代码


查看其 Java 代码 :

public final class TypealiasKt {
   @NotNull
   public static final byte[] getBinary(@NotNull String string) {
      Intrinsics.checkParameterIsNotNull(string, "string");
      Charset var2 = Charsets.UTF_8;
      boolean var3 = false;
      byte[] var10000 = string.getBytes(var2);
      Intrinsics.checkExpressionValueIsNotNull(var10000, "(this as java.lang.String).getBytes(charset)");
      return var10000;
   }
}
复制代码


代码中根本找不到类型别名 Binary 的踪影。经过编译之后,类型别名会被原类型直接替换。这仅仅只是 Kotlin 丰富的语法糖之一,编译器在其中做了一些手脚。


typealias 存在的意义是什么 ?



现在,你估计更加困惑了。

开发者手动声明一个类型别名,编译器再自动替换回原类型。意义何在?

唯一能想到的一点大概只有 "代码可读性" ,这里的代码可读性还要打上了一个大大的引号。


复杂的业务逻辑下,你的代码中可能会出现超长命名,多参数,多泛型类型的类名,接口名,函数名。

typealias FileTable<K> = MutableMap<K, MutableList<File>>
typealias OnPermissionResult = ActivityCompat.OnRequestPermissionsResultCallback
typealias SimpleName = LonglonglonglonglonglonglonglonglonglonglonglonglonglonglongName
复制代码


用类型别名来代替原本可读性不好(名字太长或者声明复杂)的类型名,这可能就是 typealias 的主要作用。

至于到底有没有提升可读性?我觉得这是有代价的。因此而丧失的是直观的类型声明。以上面代码块中的 FileTable 为例,一眼看过去,你能发现它是一个 MutableMap<K, MutableList<File>> 吗?肯定不能。特别在团队开发中,除了这个代码的贡献者,可能每一位都要到类型别名声明处进行查看。


有人可能也会有不一样的声音。统一的全局声明很正常,而且也方便做统一修改,避免到代码使用处一一修改。况且 IDE 都会自动提示类型别名的声明。没有不使用 typealias 的道理。


所以,这是一个仁者见仁,智者见智的问题。你觉得有用就可以使用,任何一门技术肯定都有它的使用场景,并没有必要去过多争论。


我用协程还是用 RxJava? 我用 LiveData 还是用事件总线? 我用 ViewBinding 还是 DataBinding ? ......

这几个问题可能比较常见,但是上面的每一组选择,如果你真正深入了解其技术本质的话,就会发现它们的使用场景并不一样,也就不会存在 如何选择 的疑问了。


typealias 使用注意事项


有点跑偏了,再回到 typealias 。后面再说一些 typealias 的注意事项,内容会比较零散,后续也可能继续增加。


typealias 可以写在哪里?

只能声明在文件顶层位置,其他任何地方都不行。


与 Java 如何交互?

拒绝和 Java 进行交互。


禁止套娃!

首先我们是可以 给别名取别名 的,如下所示:

typealias Names<T> = Array<T>
typealias SubNames<T> = Names<T>
复制代码


虽然没有太大意义,但是语法上是正确的。

下面这样套娃肯定是不行的。

typealias R = R
typealias L = List<L>
typealias A<T> = List<A<T>>
typealias R1 = (Int) -> R2
typealias R2 = (R1) -> Int
复制代码


上面的每一行代码都是无法编译的。


可见性

顶层位置的 typealias 可以使用可见性修饰符 publicprivateinternal 。同时,typealias 不能修改原有类型的可见性。举个例子:

private class LongName{}
typealias ShortName = LongName // 'public' typealias exposes 'private' in expanded type LongName
复制代码


上面的代码会编译失败, public 的类型别名无法应用在 private 的原类型上。类型别名和原类型的可见性必须保持一致。


导入同名类的处理

对于在同一个类中导入两个同名类,通常的做法是, import 其中一个类,另一个使用全限定名。如下所示:

fun main() {
    val user1 = User()
    val user2 = com.test2.User()
}
复制代码


这样或多或少不大美观,可以使用 typealias 处理一下。

typealias User2 = com.test2.User
fun main() {
    val user1 = User()
    val user2 = User2()
}
复制代码


另外, import ... as ... 也可以解决这个问题。

import com.test1.User
import com.test2.User as User2
fun main() {
    val user1 = User()
    val user2 = User2()
}
复制代码


最后


不妨翻翻你的代码库,看看有没有可以使用 typealias 进行优化的 “烂” 命名。如果有的话,欢迎来到评论区交流分享。



相关文章
|
3月前
|
安全 Java Android开发
Kotlin字符串秘籍:解锁高效处理与创意应用,让你的代码闪耀不凡!
【8月更文挑战第2天】Kotlin是一门现代化的静态类型语言,以简洁、安全及强互操作性著称,在Android及服务器端开发中广受好评。本文通过与其他语言对比,深入解析Kotlin中字符串的基础和高级用法。Kotlin简化了字符串拼接,支持直接使用`+`操作符,并引入了直观的字符串模板。它提供了丰富的字符串操作函数,如使用索引范围进行子字符串提取,增强了代码的可读性。Kotlin字符串的不可变性提升了程序稳定性。利用扩展函数特性,可以轻松定制字符串行为,提高代码的模块化和重用性。掌握这些技巧能显著提升开发效率和代码质量。
42 1
|
6月前
|
前端开发 程序员
前端知识笔记(四十四)———为什么要学代码
前端知识笔记(四十四)———为什么要学代码
63 0
|
存储 Java 编译器
带你踏入kotlin大门(二)|基本功_变量篇
本篇正式开启 kotlin 的学习历程,带你正式踏入 kotlin 大门。
带你踏入kotlin大门(二)|基本功_变量篇
|
设计模式 安全 Java
带你踏入Kotlin大门(六)|基本功_接口和特殊类篇
这篇文章是我们 Kotlin 基础章系列的最后一文了,前面我们利用了5篇文章讲解了 Java 开发者如何学好 Kotlin 的基本用法,每篇文章的篇幅都不长,可以在空余时间快速阅读,笔者希望学习完前五篇,再来学习这最后一篇文章,会更加的容易理解。
|
消息中间件 算法 前端开发
Kotlin可能带来的一个深坑,实战篇
Kotlin可能带来的一个深坑,实战篇
Kotlin可能带来的一个深坑,实战篇
|
消息中间件 JavaScript 前端开发
炸裂!手摸手教你如何吃透一个 Java 项目,yyds
炸裂!手摸手教你如何吃透一个 Java 项目,yyds
218 0
炸裂!手摸手教你如何吃透一个 Java 项目,yyds
|
Java
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(4)
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏
101 0
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(4)
|
Java 编译器 程序员
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(10)
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏
102 0
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(10)
|
存储 Java 测试技术
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(2)
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏
129 0
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(2)
|
Java 编译器
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(11)
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏
107 0
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(11)
下一篇
无影云桌面