翻译说明:
原标题: Parameters and Arguments: An Easy Way to Remember the Difference
原文地址: https://typealias.com/guides/parameters-arguments/
原文作者: Dave Leeds
简述(俗称扯皮):
最近在深入研究Kotlin中的泛型语法,发现它和Java中的泛型有着很大不同,在语法上Kotlin的泛型也会比Java中的使用起来更安全。但是发现Kotlin泛型一时间冒出了很多的名词术语,什么协变、逆变、不变、星投影啊;什么实化参数、类型形参、类型实参啊,什么类、类型、子类、子类型、超类型弄得是一脸懵逼. 不过还好基本都弄懂了,于是乎想准备一系列关于Kotlin中泛型的博客来记录我的坎坷,希望能有所帮助。
以下是我之前的一些关于Kotlin的文章,如感兴趣欢迎查看:
翻译系列:
- [译]Kotlin中是应该定义函数还是定义属性?
- [译]如何在你的Kotlin代码中移除所有的!!(非空断言)
- [译]掌握Kotlin中的标准库函数: run、with、let、also和apply
- [译]有关Kotlin类型别名(typealias)你需要知道的一切
- [译]Kotlin中是应该使用序列(Sequences)还是集合(Lists)?
- [译]Kotlin中的龟(List)兔(Sequence)赛跑
- [译]Effective Kotlin系列之考虑使用静态工厂方法替代构造器(一)
- [译]Effective Kotlin系列之遇到多个构造器参数要考虑使用构建器(二)
原创系列:
- 有关Kotlin属性代理你需要知道的一切
- 浅谈Kotlin中的Sequences源码解析
- 浅谈Kotlin中集合和函数式API完全解析-上篇
- 浅谈Kotlin语法篇之lambda编译成字节码过程完全解析
- 浅谈Kotlin语法篇之Lambda表达式完全解析
- 浅谈Kotlin语法篇之扩展函数
- 浅谈Kotlin语法篇之顶层函数、中缀调用、解构声明
- 浅谈Kotlin语法篇之如何让函数更好地调用
- 浅谈Kotlin语法篇之变量和常量
- 浅谈Kotlin语法篇之基础语法
实战系列:
- 用Kotlin撸一个图片压缩插件ImageSlimming-导学篇(一)
- 用Kotlin撸一个图片压缩插件-插件基础篇(二)
- 用Kotlin撸一个图片压缩插件-实战篇(三)
- 浅谈Kotlin实战篇之自定义View图片圆角简单应用
今天是一篇简单的博客翻译作为Kotlin泛型语法系列开场白:
进入正题(开始翻译啦...)
你是否曾经很难记住形参和实参的区别,今天这篇文章就是为了量身打造的。理清它们之间的区别可以帮助你更好地深入理解函数,此外甚至可以更多帮助你理解泛型。
形式参数与实际参数
- 定义在里面就是形式参数(形参)
- 定义在外面就是实际参数(实参)
回忆两者之间的差异的最简单的简单的方法就是将arugment单词与outside单词关联起来,记住这句话
“Take your argument outside!”
这里有我画的一张卡通图帮你去记住它:
有关更多说明,让我们看一下函数和泛型类的示例。
函数中的形参与实参
这是一个对整数进行平方的简单函数。将传递哪些数据?只是我们想要平方的数字。
fun square(number: Int): Int {
return number * number
}
在这个函数的定义里面,我们说number就是一个形式参数。
现在已经定义好了我们的函数,当我们调用它时,会将一些数据传递给这个函数。
val radius = 5
val area = Math.PI * square(radius)
这里,在函数定义的外面,我们说radius是square函数的实际参数。
泛型中的形参与实参
泛型类是具有一种或多种类型的类。例如,这里有个非常简单的Box
类,它只包含其他一些对象。
class Box(var item: T)
这里,在Box类定义的里面,我们说T
是一个类型形参。
使用这个类十分简单,我们只需要调用它的构造器,并传入一些符合正确类型的数据进去即可。
val box = Box("Hello")
这里,在Box类定义的外面,我们使用String
类型实参去构造它。
事实上,Kotlin做了一些智能的类型推导,所以我们甚至不必明确指定它:
val box = Box("Hello")
在这种情况下,它仍然有一个String
类型的类型实参。它只是隐含在我们传递给构造函数的“Hello”实际参数的类型中而已。
总结
再次说明下,在函数和泛型类/接口两种情况下,结论是:
- 形参 - 在定义里面
- 实参 - 在定义外面
读者有话说
其实这篇文章目的很简单就是帮助理解一个问题:Kotlin中泛型形参和实参区别。因为这两个术语将会在后续泛型文章中被多次提到。所以如果不提前弄懂这些名词概念,后面一些深入的东西理解起来会非常吃力的。我补充几点:
- 第一: 为了验证一下理论,我们一起分析下下面两个例子:
class StringList: List{
...
}
对于以上例子很好理解,StringList类实现List接口,提供了具体的类型实参: String,可以看到String明显是在List接口定义的外面,所以它就是类型实参
class ArrayList: List{
...
}
关于这个例子,大家看看怎么分析呢?? 实际上很简单就按照这篇文章作者说的那样抓住问题关键点: 类型参数在类或接口定义外面还是里面,里面就是形参,外面就是实参
所以,这里例子很容易分析出这个T是List接口的类型实参,而不一样的是这个T同时还是ArrayList这个类的类型形参。ArrayList类中定义了自己的类型形参T,并把指定为父类(List接口)类型的实参。在Kotlin的泛型中有这样规定: 如果一个类继承泛型类(或者实现了泛型接口),就必须为基础类型的泛型形参指定一个泛型实参。它可以是具体的类型或者另一个类型形参。所以你会发现ArrayList 中的T和List中的T实际上有所不一样的,就是类型形参和类型实参的区别。
- 第二、初学者很容易走进一个误区,认为类似T,K,V这种没有具体意义的类型就是类型形参,而像String,Int这种具体有意义的类型就是类型实参。这种理解是错误,总之可以按照原文作者那样理解。
- 第三,在Kotlin中规定所有的泛型实参需要显示的声明,要么能被编译器智能推导出来。也就是不能像Java中那样直接一个List(Java中原生态类型)就行,不用指定泛型实参,这种情况在Kotlin中不允许的,编译器就会报错。所以不得不夸下Kotlin了,它语法上非常严谨和明确,不能含糊,在定义和声明就必须明确下来。
- 第四,泛型函数中也有自己的类型形参,在每次函数执行调用时类型形参会被替换成类型实参,有点函数中形参和实参。
这是泛型系列文章简单的开场白,下面将会继续深入Kotlin泛型相关内容,例如泛型擦除以及实化类型参数,泛型协变,逆变,星投影。欢迎继续关注~~
欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不定期翻译一篇Kotlin国外技术文章。如果你也喜欢Kotlin,欢迎加入我们~~~