其实官网文档很棒棒啊,http://www.kotlincn.net/docs/reference/android-overview.html
我为啥自己还要写个解释不全说不定还不准的渣文呢?好记性不如烂笔头啊
首先,Kotlin是运行在Jvm上的静态语言,源自Scala和java的结合,特性?
空类型安全、Lambda表达式、扩展方法、类型推导、没有分号、不需要new等等…
HelloWorld?
使用IDEA,可以直接新建kotlin的项目,选JVM的项目,一路next,新建一个kotlin文件,怎么写?
fun main(args:Array<out String>){ println("hello World!") }
这里的out可以省略,默认生成的也没有out
数据类:
data class Main(val id:Int,val name:String){} //调用 printlin(Main(0,"name"))
data class会直接将Main类toString覆写,所以上面的输出和ToString一样,构造方法在Main类后面的括号内
使用Gradle进行依赖管理
用IDEA新建一个Gradle项目
Java里可以直接访问kotlin的类,关于空指针,kotlin在编译时就会报错,java则会在运行时报错,解决办法是在构造参数的后面加上?号,就像这样
data class Main(val id:Int,val name:String?){}
将Java代码复制到kt文件中IDE会自动提示转换代码, 但是转换出来的代码规范性和可读性都是没有那么好的。
java中自带反射的相关类,但是在kotlin中需要自己添加:
compile "org.jetbrains.kotlin:kotlin-reflect:1.2.20"
在代码中可以这样用
class HelloKotlin{ fun hello(){} }
调用时
HelloKotlin::class.constructors.map(::println)
会输出构造函数的名字
集合遍历
上面的主函数,参数里那个out,是指泛型的extend,指继承者
.map{}是一个扩展方法,给所有可迭代的类型提供。比如Array、Collection
.map(::println)实际上是这样的,其传入的是一个lambda表达式
args.map{ println(it) } //上面也是这个意思
for (arg in args) println(arg)
这个it就是每个可迭代的项,::是指方法的引用,在java里是这样的
for(String arg:args){ System.out.println(arg); }
所以你应该明白了。
另外没有返回值返回的类型是Unit类型所以main方法可以这样
fun main(args:Array<out String>:Unit){ printlin("hello World!") }
扁平化集合flatmap
还是main方法
fun main(vararg args:String){ }
这是可变参数vararg,Java中是 …
接下来从控制台输入点什么,比如A_B_C输出A B C,Java是这样写的
public static void main(String[] args) { for (String a:args){ String[] splits=a.split("_"); for (String sp:splits){ System.out.println(sp); System.out.println(" "); } } }
kotlin是这样的
fun main(vararg args:String){ args.flatMap { it.split("_") }.map { print("$it ") } }
split中传入的是正则哦,对比下应该就清楚kotlin是啥意思了,flatMap也是一个扩展方法,刚刚是不是提到控制台参数,估计还是有人不知道main怎么传参吧,上图
另外打印中的$是字符串模板,$it可以输出其值,当然你也可以输出一些属性值,不过得加上一些东西
比如${it.length}
关于flatMap,这个在java里也是有的,但是开头说到和Scala有点像是为什么呢?你可以看看这篇文章
你会发现 kotlin真的很像,但是写法上又更像java
枚举、When表达式
枚举:
enum class Lang{ ENGLISH, CHINESE; //伴生对象, 类和伴生对象是一一对应的 companion object { //这里相当于一个静态方法 fun parse(name:String):Lang{ return Lang.valueOf(name.toUpperCase()) } } }
用法?静态方法和枚举该怎么用就怎么用咯,main方法中传参
fun main(args: Array<String>): Unit { if (args.size==0)return val lang=Lang.parse(args[0]) //直接用 不new println(lang) //不要封号 }
提一提构造方法,控制台传入chinese
fun main(args: Array<out String>): Unit { if (args.size==0)return val lang=Lang.parse(args[0]) println(lang) lang.sayHello() } enum class Lang(val hello:String){
ENGLISH("Hello"), CHINESE("你好"); //var是变量 val是常量 var msg:String = "" //伴生对象, 类和伴生对象是一一对应的 companion object { //这里相当于一个静态方法 fun parse(name:String):Lang{ return Lang.valueOf(name.toUpperCase()) } } //构造方法的方法体 init { //这里是可以访问到hello的 msg= hello; } fun sayHello(){ println(msg) println(hello) } }
输出都是一样的。
[扩展方法是一个重要的特性,可以不修改原始类,进行方法添加]如果想给Lang加上扩展方法呢?在普通方法前面加上类名.就好了
fun Lang.sayBye(){ }
那么when怎么用?
fun describe(obj: Any): String = val res=when (obj) { 1 -> "One" "Hello" -> "Greeting" is Long -> "Long" !is String -> "Not a string" else -> "Unknown" } println(res)
从上可见,when可以匹配任意类型,并且是有返回值的,更多的看这里(超乎你想象的强大):
http://www.kotlincn.net/docs/reference/basic-syntax.html#using-when-expression
文件读取?
val text = File(ClassLoader.getSystemResource("input.txt").path).readText()
就是这么简单
Retrofit2在kotlin中的使用
interface GitHubService{ @GET("https://api.github.com/repos/bboylin/UniversalToast/stargazers") fun getStartGazers(): Call<List<User>> } data class User(val login:String,val id:Long,val avatar_url:String) object Service{ val gitHubService:GitHubService by lazy { Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .baseUrl("https://api.github.com") .build().create(GitHubService::class.java) } } fun main(args: Array<String>) { Service.gitHubService.getStartGazers().execute() .body()!!.map { println(it) } }
记得加上依赖
compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0'
噢,对了覆写方法应该怎样呢?
data class User(val login:String,val id:Long,val avatar_url:String){ override fun toString(): String { return super.toString() } }
其实差不多嘛,但是上面那个!!是啥呢?如果你去掉后,IDE会这样提示
查了下,是这个意思:
非空断言运算符( !! )将任何值转换为非空类型,若该值为空则抛出异常。
你可以写为?,然后意思是可以为空。用?号时为空的话,如果没数据,就输出空白,用!!则会是异常。
这是Kotlin的另外一个新特性,空安全
尾递归
/*原始方法 会栈溢出 fun factorail(num:Int): BigInteger { if(num==0) return BigInteger.valueOf(1L); return BigInteger.valueOf(num.toLong()).times(factorail(num-1)) } fun main(args:Array<String>){ print(factorail(10000)) } */ /** * BigInteger= BigInteger.valueOf(1L)是当不赋值时默认的空构造函数会初始化value为1 */class Result(var value:BigInteger= BigInteger.valueOf(1L)) //tailrec是作为了一个标记符,加上后就会自动进行尾递归优化 tailrec fun factorial(num:Int,result: Result){ if(num==0) result.value=result.value.times(BigInteger.valueOf(1L)) else{ result.value=result.value.times(BigInteger.valueOf(num.toLong())) factorial(num-1,result) } } /** * val常量 var变量 */fun main(args: Array<String>) { val result=Result() factorial(10000,result) print(result.value) }
Kotlin中的单例模式
懒加载 线程不安全版 需要的时候再去初始化
class LazyNotThreadSafe { //kotlin原版-------------------------------- companion object{ val instance by lazy(LazyThreadSafetyMode.NONE) { LazyNotThreadSafe() } //JAVA翻译版-------------------------------- //下面是另一种等价的写法, 获取单例使用 get 方法 private var instance2: LazyNotThreadSafe? = null fun get() : LazyNotThreadSafe { if(instance2 == null){ instance2 = LazyNotThreadSafe() } return instance2!! } } }
java:
双重校验版 线程安全
class LazyThreadSafeDoubleCheck private constructor(){ //----------------原生版 companion object{ val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED){ LazyThreadSafeDoubleCheck() } //-----JAVA翻译版 private @Volatile var instance2: LazyThreadSafeDoubleCheck? = null fun get(): LazyThreadSafeDoubleCheck { if(instance2 == null){ synchronized(this){ if(instance2 == null) instance2 = LazyThreadSafeDoubleCheck() } } return instance2!! } } }
java:
内部静态类 线程安全
class LazyThreadSafeStaticInnerObject private constructor(){ companion object{ fun getInstance() = Holder.instance } private object Holder{ val instance = LazyThreadSafeStaticInnerObject() } }
java:
线程同步懒加载 线程安全 加同步锁,每次都会加锁,浪费资源
class LazyThreadSafeSynchronized private constructor() { //伴生对象 companion object { private var instance: LazyThreadSafeSynchronized? = null @Synchronized fun get(): LazyThreadSafeSynchronized{ if(instance == null) instance = LazyThreadSafeSynchronized() return instance!! } } }
java:
懒人写法 类加载的时候初始化实例 会拖慢启动速度
object PlainOldSingleton { }
java里是这样的
新的枚举
密封类用来表示受限的类继承结构:当一个值为有限集中的类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。
官方链接:http://www.kotlincn.net/docs/reference/sealed-classes.html
要声明一个密封类,需要在类名前面添加 sealed
修饰符。虽然密封类也可以有子类,但是所有子类都必须在与密封类自身相同的文件中声明。
扩展密封类子类的类(间接继承者)可以放在任何位置,而无需在同一个文件中。
//定义播放状态 枚举 每种类型只有一个实例 enum class State{ IDLE,PAUSED,PLAYING } //播放控制 sealed另外一种限定状态个数的办法 sealed class PlayerCmd{ //一个实例用object 多个用class class Play(val url:String, val position:Long=0):PlayerCmd() class Seek(val position:Long):PlayerCmd() //因为没有参数所以用Obj就好了 object Pause:PlayerCmd() object Resume:PlayerCmd() object Stop:PlayerCmd() } //实现类 by class Player { //捕捉状态变化 private var state: State by Delegates.observable(State.IDLE, { prop, old, new -> println("$old -> $new") //?使得为空时不会执行 onPlayerStateChangedListener?.onStateChanged(old, new) }) private fun sendCmd(cmd: PlayerCmd) { when (cmd) { is PlayerCmd.Play -> { println("\nPlay ${cmd.url} from ${cmd.position}ms") state = State.PLAYING doPlay(cmd.url, cmd.position) } is PlayerCmd.Resume -> { println("\nResume. ") state = State.PLAYING doResume() } is PlayerCmd.Pause -> { println("\nPause. ") state = State.PAUSED doPause() } is PlayerCmd.Stop -> { println("\nStop.") state = State.IDLE doStop() } is PlayerCmd.Seek -> { println("\nSeek to ${cmd.position}ms, state: $state") } } } private fun doPlay(url: String, position: Long) { TODO("Play function not yet implemented") } private fun doResume(){ //todo } private fun doPause() { //todo } private fun doStop() { //todo } //region api interface OnPlayerStateChangedListener { fun onStateChanged(oldState: State, new: State) } var onPlayerStateChangedListener: OnPlayerStateChangedListener? = null fun play(url: String, position: Long = 0) { sendCmd(PlayerCmd.Play(url, position)) } fun resume() { sendCmd(PlayerCmd.Resume) } fun pause() { sendCmd(PlayerCmd.Pause) } fun stop() { sendCmd(PlayerCmd.Stop) } fun seekTo(position: Long) { sendCmd(PlayerCmd.Seek(position)) } //endregion } fun main(args: Array<String>) { val player: Player = Player() player.play("http://xx.m4a") player.pause() player.resume() player.seekTo(30000) player.stop() }
泛型
http://www.kotlincn.net/docs/reference/generics.html
注解
说明:
http://www.kotlincn.net/docs/reference/annotations.html
使用:http://www.kotlincn.net/docs/reference/kapt.html
Java访问Kotlin
定义一个数据类
data class Person(var name:String)
然后在Java中调用:
public static void main(String[] args) { Person person=new Person("name"); person.getName(); person.setName("other"); }
如果改成
data class Person(var name:String,@JvmField var age:Int)
然后在Java中的时候age这种属性就没有了set、get了,因为@JvmField使得属性直接暴露出来了,同时该属性不能声明为private,并且不能自定义get set
单例?
object Singleton{ fun printlnHello(){ println("Hello") } }
编译后看生成的字节码就会知道,已经帮你写好了单例模式,所以是可以直接用的
public static void main(String[] args) { Singleton.INSTANCE.printlnHello(); }
kotlin方法的默认参数
class Overloads{ fun oberloads(a:Int,b:Int=0,c:Int=1){ print("$a $b $c") } }
在java中此时你是无法享受默认参数的便利的,需要这样改下
class Overloads{ @JvmOverloads fun oberloads(a:Int,b:Int=0,c:Int=1){ print("$a $b $c") } }
加上注解,就可以了
public static void main(String[] args) { Overloads overloads = new Overloads(); overloads.oberloads(1); }
上图更直接
Kotlin可以将方法定义在包当中
Kotlin扩展方法的调用
fun String.isEmpty():Boolean{ return this!="" }
java中使用:编译后会以类文件名+Kt的方式去生成一个可直接调用的类
public static void main(String[] args) { MainKt.isEmpty(""); }
internal关键字
会使java无法访问到kotlin中被此关键字修饰的内容
Kotlin访问Java
korlin访问java中的类属性不再具有get/set,将只会显示属性
空安全问题:
fun main(args: Array<String>) { val data = Data() val s = data.value println(s) }
java:
public class Data { public String getValue(){ return null; } }
此时直接输出null,如果给val s指定类型,比如val s:String=data.value,此时编译就会报错,此时value被称为平台类型,此时编译器会不care,大多数时候需要你自己判断定义,以及觉得加不加?号 还有 !!
泛型:java中的?变成了*,上下限变成了out in,java可选带泛型,kt必须有泛型参数,因此可能无限循环
Kotlin中Any就是Object
线程与同步:Kotlin中方法Synchronized变成了注解方式,代码块变成了方法,同时Volatile也变成了注解
最后,这个笔记实在是做不下去了,以上内容来自视频:
https://github.com/enbandari/Kotlin-Tutorials
其仓库中包含大量kotlin文章,以及其公众号的推送,值得一看,目测是鹅厂大佬
入门,应该大大概概差不多了,毕竟官方文档那么好,而且与java如此相似
本站由以下主机服务商提供服务支持:
0条评论