Kotlin 初窥门径[1]:基础概念
Kotlin 是由 JetBrains 开发的基于JVM的语言。JetBrains 因为创造了一个强大的Java开发 IDE(Intellij) 而被大家所熟知。Android Studio 就是基于Intellij IDEA 的开源社区版,今年的I/O大会上谷歌宣布 Kotlin 正式成为 Android 的官方语言。
Kotlin 是使用 Java 开发者的思维被创建的,相比于 JAVA 具有如下优势:
- 简洁,通过支持 variable type inference,higher-order functions (closures),extension functions,mixins and first-class delegation 等实现,你可以编写少得多的代码。
- 安全,在我们编译时期就处理了各种null的情况,避免了执行时异常。
- 函数式,Kotlin是基于面向对象的语言,但它使用了很多函数式编程的概念,如,使用lambda表达式来更方便地解决问题。
- 完美兼容JAVA,可以继续使用所有的你用Java写的代码和库,甚至可以在一个项目中使用Kotlin和Java两种语言混合编程。
包
为了方便管理大型软件系统中数目众多的类,解决类的命名冲突问题,类似于Java,Kotlin同样引入包(package)机制,提供类的多重类名空间。
声明包
package语句必须是文件中的第一行非注释的程序代码。
package foo.bar
fun doSomething(){} // 定义的函数
class User(){} //定义的类
源文件的所有内容(比如类和函数)都被包声明包括。 因此在上面的例子中,doSomething() 的全名应该是 foo.bar.doSomething , User 的全名是 foo.bar.User 。
如果没有任何包声明的话,则当中的代码都属于默认包,导入时包名即为函数名!
导入包
导入一个单独的名字
import foo.Bar
导入范围内的所有可用的内容 (包, 类, 对象, 等等):
import foo.*
如果将两个含有相同名称的的类库以 "*" 同时导入,将会存在潜在的冲突。例如:
import net.mindview.simple.*
import java.until.*
由于java.util.和net.mindview.simple.都包含Vector类。在创建一个Vector类时,编译器并不知道调用哪个类库的类,从而会报错误信息。
而此时就是 Kotlin 中强大的 as
关键字大显神威的时候了,此关键字用于局部重命名,以解决冲突。
import net.mindview.simple.Vecotr
import java.until.Vecotr as aVector
val v : Vecotr = Vector()
val vA : aVector = aVector()
import 关键字不局限于导入类,您也可以使用它来导入其他声明:
- 顶级函数与属性
- 在对象声明中声明的函数和属性
- 枚举常量
类
Kotlin中的类遵循一个简单的结构。尽管与Java有一点细微的差别。你可以使用 try.kotlinlang.org 在不需要一个真正的项目和不需要部署到机器的前提下来测试一些简单的代码范例。
定义一个类
如果你想定义一个类,你只需要使用 class
关键字。
class Person {
}
它具有一个默认唯一的无参构造函数,如果我们想使用有参的参构造函数,只需要在类名后面写上它的参数:
class Person (name: String, var age: Int) {
init {
// init
}
}
而 init
方法是构造函数的函数体,可以用来执行一些初始化的工作。
在上面的代码时,我们并没有使用
;
结尾,当然你也可以使用分号结尾,但这不是必须的,官方也建议不使用分号,这是一个很好的实践,可以节约你很多时间。
访问性修饰符
Kotlin中这些修饰符是与C#中的使用是有些不同的。在 kotlin 中默认的修饰符是 public ,这节约了很多的时间和字符。而 kotlin 中具有以下几种访问性修饰符:
private 表示它只能被自己所的在文件可见,我们就不能在定义这个类之外的文件中使用它(但在同一个文件中的其它类可以访问,与C#中不同)。
protected 这个修饰符只能被用在类或者接口中的成员上,一个包成员不能被定义为 protected 。它可以被成员自己和继承它的成员可见(比如,类和它的子类)。
internal 如果是一个定义为 internal 的包成员的话,对所在的整个 module 可见。如果它是一个其它领域的成员,它就需要依赖那个领域的可见性了。比如,如果我们写了一个 private 类,那么它的 internal 修饰的函数的可见性就会限制与它所在的这个类的可见性。
public 这是默认的修饰符,成员在任何地方被修饰为 public ,很明显它只限制于它的领域。一个定义为 public 的成员被包含在一个 private 修饰的类中,这个成员在这个类以外也是不可见的。
什么是 module ?
根据Jetbrains的定义,一个 module 应该是一个单独的功能性的单位,它应该是可以被单独编译、运行、测试、debug的。根据我们项目不同的模块,可以在Android Studio中创建不同的 module 。在Eclipse中,这些 module 可以认为是在一个 workspace 中的不同的 project 。
构造函数
在 Kotlin 中类可以有一个主构造函数以及多个二级构造函数。主构造函数是类头的一部分:跟在类名后面(可以有可选的类型参数)。
class Person constructor(name: String) {
}
如果主构造函数没有注解或可见性说明,则 constructor 关键字可以省略:
class Person(name: String){
}
所有构造函数默认都是 public 的,它们的类是可见的,可以被其它地方使用,我们也可以把构造函数修改为 private :
class Person private constructor(name: String) {
}
同样,也可以在构造函数中声明属性的访问级别:
class A private constructor(private var a: Int) {
}
在构造函数中声明的参数,如果没有使用 var 修饰时,则表示为局部变量,而使用 var 时则是类的属性。
类也可以有二级构造函数,需要加前缀 constructor:
class Person(var name: String){
constructor(name: String, age: Int) : this(name) {
}
}
需要注意的时候的是,如果主构造函数带有参数,则二级构造函数必须显式的调用主构造函数。
创建一个实例
我们可以像使用普通函数那样使用构造函数创建类实例:
var persion1 = Persion("aaa");
var persion2 = Persion("aaa",10);
在 Kotlin 中并没有
new
关键字。
继承
默认任何类都是继承自 Any
,与 C# 中的 Object 类似。而在 Kotlin 中,所有的类默认是不可继承的,只有那些声明为 open
或者 abstract
的类才可以被继承:
open class Person(name: String, age: Int) {
}
class Student(name: String, age: Int, score: Int) : Persion(name, age) {
}
枚举
Kotlin也提供了枚举( enums )的实现:
enum class Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY,THURSDAY, FRIDAY, SATURDAY }
枚举可以引用参数:
enum class Icon(val res: Int) {
UP(R.drawable.ic_up),
SEARCH(R.drawable.ic_search),
CAST(R.drawable.ic_cast)
}
val searchIconRes = Icon.SEARCH.res
枚举可以通过 String 匹配名字来获取,我们也可以获取包含所有枚举的 Array ,所以我们可以遍历它。
val search: Icon = Icon.valueOf("SEARCH")
val iconList: Array<Icon> = Icon.values()
而且每一个枚举都有一些函数来获取它的名字、声明的位置:
val searchName: String = Icon.SEARCH.name()
val searchPosition: Int = Icon.SEARCH.ordinal()
枚举还根据它的顺序实现了 Comparable 接口,所以可以很方便地把它们进行排序。
变量和属性
在 Kotlin 中,一切都是对象。没有像C#中那样的原始基本类型。这个是非常有用的,因为我们可以使用一致的方式来处理所有的可用的类型。
基本类型
当然,像integer,float或者boolean等类型仍然存在,但是它们全部都会作为对象存在的。基本类型的名字和它们工作方式都是与C#非常相似的,但也有一些不同:
- 数字类型中不会自动转型。比如,你不能给 Double 变量分配一个 Int 。必须要做一个明确的类型转换
toDouble()
。 - 字符(Char)不能直接作为一个数字来处理。我们需要把他们转换为一个数字
'a'.toInt()
。 - 位运算也有一点不同,在C#中我们经常使用
|
和&
,但在 Kotlin 中则使用or
和and
关键字。
变量
变量分为 可变(var
) 和不可变(val
) 两种,val
类似于C#中的readonly。
一个不可变对象意味着它在实例化之后就不能再去改变它的状态了,如果大部分的对象都是可变的,那就意味着任何可以访问它这个对象的代码都可以去修改它,从而影响整个程序的其它地方。
不可变对象也可以说是线程安全的,因为它们无法去改变,也不需要去定义访问控制,因为所有线程访问到的对象都是同一个。
而变量的声明和Typescript
非常相似:
var a = "aaaaaaaaaa"
var b: String = "bbbbbbbbbb"
val c: = 10
当我们在声明中就为变量赋值时可以不用指定类型,因为 kotlin 会自动推断出它的类型,这样可以让代码更加清晰和快速修改。
属性
属性做的事情就是为字段加上 getter 和 setter,在C#中我们可以使用 public string a { get; set; }
的方式来声明一个属性,而在 kotlin 中,一个变量就是一个拥有默认getter和setter的属性,我们也可以自定义getter和setter:
public class Person {
var name: String = "zs"
get() = field.toUpperCase()
set(value) {
field = value
}
}
函数
定义
函数使用 fun
关键字来定义:
fun test(x: Int) {
}
如果没有指定返回值,则会返回一个 kotlin.Unit
,与C#中的 void
类似,但 kotlin.Unit
是一个真正的对象。而指定返回值也很简单:
fun test(x: Int) : Boolean {
return x > 0
}
而上面的代码还有一种更简单的方式,类似C#中的表达式方法体:
fun test(x: Int) : Boolean = x > 0
参数默认值
参数默认值在C#中比较常见,而在 Kotlin 中也支持了参数默认值:
fun test(x: Int, y: String = "default value") {
}
我们为第二个参数设置了一个默认值,这样,在调用的时候便可以不传第二个参数:
test(1)
test(1,"yyyyyyyy")
扩展函数
扩展函数数是指在一个类上增加一种新的行为,甚至我们没有这个类代码的访问权限。这是一个在缺少有用函数的类上扩展的方法。
在C#中,我们可以使用一个静态类中的静态方法,把要扩展的类型使用this修饰,做为第一个参数传进来,而 kotlin 中,更加简洁:
class Persion {
fun test1() {
}
}
class MyClass {
// 扩展Persion类
fun Persion.test2() {
}
fun test() {
var persion = Persion()
persion.test1()
// 调用扩展方法
persion.test2()
}
}
就像定义一个普通函数一样,只需要把函数名定义为要扩展的类型加上要扩展方法即可。
而扩展函数也可以是一个属性,可以通过相似的方法来扩展属性:
public var Persion.name: String
get() = getName()
set(v) = setName(v)
数据类
数据类是一种非常强大的类,它可以让我们避免创建C#中用于保存状态但又操作非常简单的POCO类模版代码。它们通常只提供了用于访问它们属性的简单的getter和setter。定义一个新的数据类非常简单:
data class UserDto(val name: String, val age: Int, val email: String, var date: Date)
额外的函数
通过数据类,我们可以方便地得到一些非常有用的函数:
- equals(): 它可以比较两个对象的属性来确保他们是相同的。
- hashCode(): 我们可以得到一个hash值,也是从属性中计算出来的。
- copy(): 你可以拷贝一个对象,可以根据你的需要去修改里面的属性。
- 一系列可以映射对象到变量中的函数。
映射对象到变量中
映射对象的每一个属性到一个变量中,这个过程是非常枯燥的。而在 kotlin 中则非常简洁:
var user = UserDto('zs', 18, Date())
val (name, age, email) = user
上面这个多声明会被编译成下面的代码:
val name = user.component1()
val age = user.component2()
val email = user.component3()
在主构造函数中有多少个参数,就会依次生成对应的component1,component2,component3……这些函数返回的就是对应字段的值。
这个特性背后的逻辑是非常强大的,它可以在很多情况下帮助我们简化代码。举个例子, Map 类含有一些扩展函数的实现,允许它在迭代时使用key和value:
for ((key, value) in map) {
Log.d("map", "key:$key, value:$value")
}
Kotlin 初窥门径[1]:基础概念的更多相关文章
- 【Machine Learning】机器学习及其基础概念简介
机器学习及其基础概念简介 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...
- TCP/IP基础概念及通信过程举例
TCP/IP基础概念及通信过程举例 出现 上个世纪60年代,由于中央集中式网络的容灾性较弱,以美国国防部为中心的一家组织研究出分组交换网络.后来为了验证分组交换技术的实用性,ARPANET出现了,并且 ...
- Jmeter基础之---jmeter基础概念
Jmeter基础之---jmeter基础概念 JMeter 介绍: 一个非常优秀的开源的性能测试工具. 优点:你用着用着就会发现它的重多优点,当然不足点也会呈现出来. JMeter 介绍: 一个非常优 ...
- 快速入门系列--WCF--01基础概念
转眼微软的WCF已走过十个年头,它是微软通信框架的集大成者,将之前微软所有的通信框架进行了整合,提供了统一的应用方式.记得从自己最开始做MFC时,就使用过Named Pipe命名管道,之后做Winfo ...
- 理解 angular2 基础概念和结构 ----angular2系列(二)
前言: angular2官方将框架按以下结构划分: Module Component Template Metadata Data Binding Directive Service Dependen ...
- JavaBean 基础概念、使用实例及代码分析
JavaBean 基础概念.使用实例及代码分析 JavaBean的概念 JavaBean是一种可重复使用的.且跨平台的软件组件. JavaBean可分为两种:一种是有用户界面的(有UI的):另一种是没 ...
- RabbitMQ基础概念详细介绍
http://blog.csdn.net/column/details/rabbitmq.html 转至:http://www.ostest.cn/archives/497 引言 你是否遇到过两个(多 ...
- linux设备驱动归纳总结(二):模块的相关基础概念【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-59415.html linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10 ...
- linux设备驱动归纳总结(一)内核的相关基础概念【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-59413.html linux设备驱动归纳总结(一):内核的相关基础概念 xxxxxxxxxxxxxx ...
随机推荐
- Django+MySQL开发项目:内容管理系统cms(一)
Baker-Miller Pink被科学方法证实可以平静情绪并且抑制食欲的颜色,具有amazing的效果.基百里面说实验结果表明该颜色具有: "a marked effect on lowe ...
- Day01_变量,数据类型_程序交互_流程控制
python执行的两种方式: 1,交互的方式: 优点:可以及时调试程序,调试方法 缺点: 无法永久保存代码 2,保存在文件中执行 优点:可以永久保存代码,在执行的时候调用 缺点:不能即时调试代 ...
- Jenkins-FQA
1.svn url不能设置成文件路径,而应该是"文件夹"路径,也就是说必须是目录. 示例: Checking out https://svn.gw.com.cn:10000/svn ...
- 关于TRIM的优化技巧
背景 今天在论坛中,看到有人在问一个千万级别表查询的优化.一个简单的查询几分钟.语句如下 SELECT work_date , major , style , ...
- python基础(7):元祖类型(赋值补充)
前面学了列表和字典,今天我们看一个和列表相似的类型元祖. 预习: 简单购物车 实现打印商品详细信息,用户输入商品名和购买个数,则将商品名,价格,购买个数加入购物列表,如果输入为空或其他非法输入则要求用 ...
- 【原创】EntityFramework Core 中使用 CodeFirst 模式时 PowerShell 版本问题及解决
一.描述: 在使用 Entity Framework Core 时,使用 CodeFirst 模式, 在 VS 中的 PMC(nuget 包管理 控制台) 控制台界面使用如下命令: Install-P ...
- luogu P1361 小猫爬山 [iddfs]
题目描述 WD和LHX饲养了N只小猫,这天,小猫们要去爬山.经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了. WD和LHX只好花钱让它们坐索道下山.索道上的缆车最大承重量为W ...
- 全站 HTTPS 没你想象的那么简单
对自己无知这件事本身的无知真的挺可怕 认知偏差现象一直存在于我们每个人身上,谁也避免不掉,不过是有的人了解这件事儿,有的人不怎么知道而已,这就产生了「无知而不自知」的认知偏差.当然,这时候你自己忽悠自 ...
- Python获取股票历史、实时数据与更新到数据库
要做量化投资,数据是基础,正所谓"巧妇难为无米之炊" 在免费数据方面,各大网站的财经板块其实已提供相应的api,如新浪.雅虎.搜狐...可以通过urlopen相应格式的网址获取数据 ...
- 网络爬虫Web开始
一.介绍 该程序主体是<Python核心编程第二版>例20.2.本篇会修改部分代码及添加了相关注释. ps:该书该例程不能直接运行,需要修改. 二.功能 网络爬虫crawl.py抓取web ...