翻译说明:
原标题: Kotlin: should I define Function or Property?
原文地址: blog.kotlin-academy.com/kotlin-shou…
原文作者: Igor Wojda
最近,我对属性和函数用法方面感到困惑。这是一个很好的机会去介绍有关Kotlin属性的概念。那么问题来了,什么时候使用函数或者使用属性呢?我建议你遵循以下最简单的规则:
- 属性是描述状态
- 函数是描述行为
让我们进一步探讨他们。属性代表着一个可以描述对象状态的数据结构,例如: Person对象可以有name、lastName 和 weight属性。
class Person (var name:String, var lastName:String, var weight:Double)
复制代码
我们还可以创建一个派生属性,用于返回fullName,它是由name和lastName两个属性组合而成的。
class Person (var name:String, var lastName:String, var weight:Double) {
val fullName = "$name $lastName"
}
复制代码
在上述例子中,在Person对象创建期间,属性值fullName仅仅被赋值一次。有时这是一个理想的行为,但是在这个例子中,当我们去改变了name或者lastName的属性值时,被存储在fullName的值不会被更新。幸运的是,Kotlin也提供了使用getter或者setter去自定义属性访问器的能力。在下面例子中fullName属性的值会在每一次被访问的时候都会被赋值。
class Person (var name:String, var lastName:String, var weight:Double) {
val fullName
get() = "$name $lastName"
}
复制代码
函数,在另一方面包含了对象所有可以被执行的行为或者动作。我们的Person类可以有 run(),walk() 和 jump() 的方法。
class Person () {
fun run() { /*doSth*/ }
fun walk() { /*doSth*/ }
fun jump() { /*doSth*/ }
}
复制代码
重要的是需要注意,该方法可能会有一个间接修改对象状态的副作用,例如我们每调用一次 jump() 方法都会使得person对象中的weight属性值减少0.1
class Person (var name:String, var lastName:String, var weight:Double) {
//..
fun jump() {
weight -= 0.1
/*doSth*/
}
}
复制代码
如果你仍然困惑于什么时候使用属性或函数,那么接下来将会从外部调用者的角度去回答这个问题。我们先暂时不管其内部的实现(一个属性被存储在哪里或者行为怎么实现的)并且需要更加简单地去看待外部的API(类似于加入第三方的库到项目中,我们仅仅关注其库提供API即可)
person.name = "Igor"
person.weight = 79
person.jump()
person.jump()
person.jump()
复制代码
使用Kotlin属性好处之一在于属性访问器更加简洁的语法(我们可以使用height 去替代getHeight()/setHeight()方法),此外就是getter和setter方法更加接近于属性的声明,不像Java,我们通常会在一个类的顶部定义成员属性,然后在其下面定义getter和setter方法。我喜欢属性委托这个特性,它可以提高复用代码的能力。我们可以初始化一个变量仅仅当它被需要使用的时候(lazy delegate) ,无论什么时候属性的值发生变化,可以执行一些动作(Observable delegate)或者使用自定义委托实现在另一对象(Android shared preferences, map, browser session, database…) )中简单地存储我们属性.
不错的是Kotlin允许我们在接口中都可以使用函数和属性
//BAD
class Person () {
private var weight:Double = 0
fun setWeight(weight:Double) {
this.weight = weight
}
fun getWeight(): Double {
return weight
}
}
//GOOD
class Person (var weight:Double)
复制代码
当我们看到一个以set为前缀(如: setHeight())开头的方法、只有一个参数并且被指定为private的变量或者以get为前缀(如: getHeight())开头的方法、返回一个被指定为private的变量时,我们应该定义一个属性来替代它。
指南: 如何做这个决定?
每次你想去声明一个新的函数时,你需要问自己两个问题:
- "它是描述一个行为吗?"-- 将描述行为的函数作为候选者,例如 run(),walk()和jump()
- "它是描述一个状态吗?"-- 将描述状态的函数作为候选者,例如name,lastName和weight
这里还有一系列额外来自Effective Java(如果没记错的话)的帮助指南让我们了解定义属性优于函数。
- 不会抛出异常
- 以简单廉价方式去计算(或者在第一次运行时被缓存)
- 在多个调用中返回相同的结果
以上的指南应该可以给你一个不错的建议,什么时候去使用函数或者属性。在一开始,定义属性或许有点不适应(特别是Java开发者),但是相信我--一段时间后,这个决定是明智的。
译者有话说:
- 1、我为什么要翻译Kotlin系列博客?
我们都知道Kotlin这门语言流行不久,目前在国内没有被大面积应用于实际项目中,而在国外这门语言则非常流行,有很多公司也在大量运用它,在今年的Google IO上,Google官方说他们有35%的工程师喜欢使用Kotlin,所以国外一些Kotlin相关的博客质量都还不错。也包括了他们使用Kotlin在一些实际项目中的经验。一门语言一定得在实际应用中体现它的价值,所以有必要去学习学习。由于国外博客都是英文的,所以我做了一些翻译的事情以及我对这边博客感受并提炼一些重要点出来。欢迎继续关注~
- 2、我为什么要翻译这篇博客?
我们知道学习Kotlin的语法时候与应用于开发是有差距的,在使用过程中会遇到各种选择的问题,比如这篇博客是阐述在Kotlin中我们什么时候去定义一个属性,什么时候去定义一个函数呢?这个问题是从基本语法学习中得不到的,得从实际需求和使用经验中去体会,对于Java转Kotlin的初学者而言,很容易受到Java语言思想去任意定义函数。因为在Java中是没有自定义属性访问器一说,改变状态唯有通过函数呗。而在Kotlin提供了一个更为简单方式去改变属性状态。所以当你还在用Java语言思想在使用Kotlin的语言,不妨真正去思考一下使用Kotlin来写优势在哪里?
- 3、这篇博客核心点提炼?
关于什么时候用函数什么时候用属性: 描述状态时,用属性;描述行为时,用函数。
看个例子再次体会下(关于自定义属性访问器的知识可以查阅我之前的浅谈系列博客中变量和常量那篇):
Java部分代码例子实现
//对于Java而言没有Kotlin中属性访问器之说,对于需要每次访问时都
//必须保证拿到最新状态时,Java中唯一好的处理方式就是定义函数,例如isIdling()、isEnded()方法
class VideoPlayer{
public boolean isIdling() {//描述的是状态
return this.mPlayer != null && this.mPlayer.getPlaybackState() == 1;
}
public boolean isEnded() {//描述的是状态
return this.mPlayer != null && this.mPlayer.getPlaybackState() == 4;
}
public void play(){//描述的是行为动作
//do sth
}
public void resume(){//描述的是行为动作
//do sth
}
}
//调用
if (mVideoPlayer.isEnded() || mVideoPlayer.isIdling()) {
mVideoPlayer.play();
}else{
mVideoPlayer.resume();
}
复制代码
Kotlin代码例子实现
class VideoPlayer{
var mIsIdling = false//描述的是状态
get() = this.mPlayer != null && this.mPlayer.getPlaybackState() == 1
var mIsEnded = false//描述的是状态
get() = this.mPlayer != null && this.mPlayer.getPlaybackState() == 4
fun play(){//描述的是行为动作
//do sth
}
fun resume(){//描述的是行为动作
//do sth
}
}
//调用
if (mVideoPlayer.mIsEnded || mVideoPlayer.mIsIdling) {
mVideoPlayer.play()
}else{
mVideoPlayer.resume()
}
复制代码
欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不定期翻译一篇Kotlin国外技术文章。如果你也喜欢Kotlin,欢迎加入我们~~~