Kotlin初探

Posted llguanli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin初探相关的知识,希望对你有一定的参考价值。

演示样例源代码传送门

前言

Kotlin是一种在 Java虚拟机上执行的静态型别编程语言。它主要是由俄罗斯圣彼得堡的JetBrains开发团队所发展出来的编程语言。

该语言有几个优势
1. 简洁
它大大降低你须要写的样板代码的数量。
2. 安全
避免空指针异常等整个类的错误。
3. 通用
构建服务器端程序、android 应用程序或者在浏览器中执行的前端程序。


4. 互操作性
通过 100% Java 互操作性,利用 JVM 既有框架和库。

配置

在我们的AndroidStudio开发工具中。要想使用Kotlin这个优秀的开发语言。我们须要安装插件,直接在安装插件界面搜索Kotlin然后安装。之后再gradle文件添加例如以下配置

apply plugin:‘kotlin-android‘
apply plugin:‘kotlin-android-extensions‘

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}

项目gradle文件

buildscript {
    ext.kotlin_version = ‘1.1.1‘
    repositories {
        jcenter()
    }
    dependencies {
        classpath ‘com.android.tools.build:gradle:2.3.1‘
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

完毕上面的配置后。我们就能够愉快的玩耍了。

Kotlin演示样例

首先我们还和曾经一样。创建一个Android项目,自己主动创建一个Activity之后我们再创建一个java类

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
public class Test {
    private static String str = null;

    public static void main(String[] args) {
        str = "Code4Android";
        System.out.println(str);
    }
}

那上面的代码假设用kotlin实现是什么样子呢。虽然如今我们还不能写出Kotlin代码。可是在安装插件后AS中提供了自己主动转换Kotlin代码的功能

技术分享图片

转换后的Kotlin代码

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val toolbar = findViewById(R.id.toolbar) as Toolbar
        setSupportActionBar(toolbar)

        val fab = findViewById(R.id.fab) as FloatingActionButton
        fab.setOnClickListener { view ->
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show()
        }
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val id = item.itemId
        if (id == R.id.action_settings) {
            return true
        }
        return super.onOptionsItemSelected(item)
    }
}


object Test {
    private var str: String? = null

    @JvmStatic fun main(args: Array<String>) {
        str = "Code4Android"
        println(str)
    }
}

注意:AS提供的java代码自己主动转换功能,我们不要轻易使用,更不要转化我们成熟的项目,假设须要就须要我们自己去重构实现。否则会有意向不到的事情等着你,毕竟转换不是那么智能。上面的代码仅仅是让你先简单熟悉下Kotlin代码时什么样子的,接下来我们先去学习一下Kotlin的基本的语法。相信非常easy上手。

Hello World!

我们由一个简单的”Hello World!”输出程序開始。与新建java文件相似,例如以下图。我们选择Kotlin File/Class.创建一个Kotlin文件。

技术分享图片

package com.learnrecord

/**
 *Created by Code4Android on 2017/4/21.
 */

var str: String = ""

fun main(args: Array<String>) {
    str = "Hello World!"
    println(str)
}

上述代码就是简单的输出一个字符串“Hello World”,package 后面跟的是包名,我们看出了和java文件的差别,在包名后面没有以分号“;”结尾。

在Kotlin语法中。语句结尾都不在有分号“;”。

在Kotlin中变量声明有两种类型。val修饰变量是仅仅读变量即仅仅能赋值一次。再次赋值时就会编译错误
,假设我们须要多次改动值就须要使用var。在上面的 var str: String = “”中,str是变量名,:String,表明该变量是String类型变量。后面就是赋值语句。我们也能够这样写var str= “”省略了生命变量类型。它能够依据赋的值而自己主动推断出类型。假设我们使用以下赋值语句str=null,发现null是不能赋值的,这就是Kotlin的特性。假设我们想赋值null,能够改动为 var str:String?=”“。


fun就是函数生命,而这个main函数就和我们java中的main方法一样。是程序执行的入口。println就是一个打印输出。

Kotlin声明类型

在Kotlin中有例如以下几种Number类型,他们都是继承自Number抽象类。
Float(32位),Double(64),Int(32),Byte(8),Short(16),Long(64,类型用大写L,如12L)。Any(随意类型),数组类型Array 依据传入的泛型数据自己主动匹配类型,Kotlin还提供了指定类型的Array,如ByteArray,CharArray,ShortArray,IntArray,LongArray,FloatArray,DoubleArray,BooleanArray。在数组类型中都提供了get(index),set(index,value)及iterator()方法供我们使用。

    val iArray: IntArray = intArrayOf(1, 2, 3)
    val sArray: Array<String> = Array<String>(3, { i -> i.toString() })
    val anyArray: Array<Any> = arrayOf(1, "2", 3.0, 4.1f) // 可将类型进行混排放入同一个数组中
    val lArray: LongArray = longArrayOf(1L, 2L, 3L)

函数

我们先来实现一个简单的数值求和的函数,通用实现方法例如以下

    fun sum(a: Int, b: Int): Int {
        return a + b
    }

传入两个Int型数值,sum是函数名,括号后面的:Int表示该函数返回Int的值,函数体中对两个数字相加并返回。在Kotlin中表达式也能够作为函数体,编译器能够推断出返回类型,能够简化为

    fun sum(a: Int, b: Int) = a + b

为了更好理解表达式能够作为函数体,我们能够创建一个函数获取两个数的最大值,例如以下:

  fun max1(a:Int,b:Int)=if (a>b) a else b

须要注意的是若if后有多个表达式,例如以下

    fun max1(a:Int,b:Int)= if (a > b) {
        println(a)
        a
    } else {
        println(b)
        //假设我们将println(b)放到b的以下,执行会返回kotlin.Unit为无类型。返回值总是最后一个表达式的返回值或值
        b
    }
    println(max1(1,3))

括号里的表达式顺序决定了返回的值及其类型。

假设我们的方法体仅仅是打印字符串,并不返回值则

    fun printInt(a: Int): Unit {
        println(a)
    }

Unit就相似我们java中的void,即没有返回值,此时我们能够省略

    fun printInt(a: Int) {
        println(a)
    }

对于函数体,方法或者类等和java一样也有一些修饰符,例如以下

  • abstract //抽象类标示
  • final //标示类不可继承。默认属性
  • enum //标示类为枚举
  • open //类可继承,类默认是final的
  • annotation //注解类
  • private //仅在同一个文件里可见
  • protected //同一个文件里或子类可见,不可修饰类
  • public //全部调用的地方都可见
  • internal //同一个模块中可见,若类不加修饰符,则默觉得该修饰符,作用域为同一个应用的全部模块。起保护作用,防止模块外被调用。

操作符

直接上代码例如以下

    //操作符  shl 以下对Int和Long
    var a: Int = 4
    var shl: Int = a shl (1)  //Java中的左移运算符 <<
    var shr: Int = a shr (1) //Java中的右移运算符 >>
    var ushr: Int = a ushr (3) //无符号右移,高位补0 >>>
    var and: Int = 2 and (4)   //按位与操作 &
    var or: Int = 2 or (4) //按位或操作 |
    var xor: Int = 2 xor (6)  //按位异或操作 ^
    print("\nshl:" + shl + "\nshr:" + shr + " \nushr:" + ushr + "\nand:" + and + "\nor:" + or + "\nxor:" + xor)

输出信息为

shl:8
shr:2 
ushr:0
and0
or:6
xor:4

在上面的部分操作符可达到逻辑操作符。 当我们使用Boolean时,or() 相当于 ||,and() 相当于 &&。 xor() 当操作符两边相反时为true,否则为false ,not()时取反。

数组遍历及控制语句

遍历数组

    fun forLoop(array: Array<String>) {
        //第一种方式直接输出字符(相似java foreach)
        for (str in array) {
            println(str)
        }
        //Array提供了forEach函数
        array.forEach {
          println(it)
         }
        //array.indices是数组索引
        for (i in array.indices) {
            println(array[i])
        }
        //(相似javafor(int i=0;i<arry.length;i++))
        var i = 0
        while (i < array.size) {
            println(array[i++])
        }
    }

使用when推断类型

fun whenFun(obj: Any) {
        when (obj) {
            25 -> println("25")
            "Kotlin" -> println("Kotlin")
            !is String -> println("Not String")
            is Long -> println("Number is Long")
            else -> println("Nothing")
        }
    }

is 和java中instanceof是一个作用推断是否为某个类型。

!is即推断不是某个类型。

//@定义label,一般用在内层循环中跳到外层循环:i in 0..2等价于java中 for(int i=0;i<=2;i++)效果
    loop@ for (i in 0..2) {
        for (j in 0..3) {
            println("i:" + i + "  j:" + j)
            if (j == 2)
            //continue@loop//跳到外层循环,继续往下执行
                break@loop  //跳到外层循环label处,跳出改层循环
        }
    }

倒序输出是downTo

    //倒序输出5 4 3 2 1 0
    for (i in 5 downTo 0) {
        println(i)
    }
    //设置输出数据步长
     for (i in 1..5 step 3) print(i) // 输出 14
     //step和downTo混合使用
     for (i in 5 downTo 1 step 3) print(i) //输出52

类与枚举

/*** constructor:构造函数
 * constructor无修饰符(如:private)时,constructor能够省略:
 * 当是无參构造函数时,整个构造函数部分也能够省略,省略的构造函数默认是public的
 * 因为primary constructor不能包括不论什么代码。因此使用 init 代码块对其初始化,
 * 同一时候能够在初始化代码块中使用构造函数的參数
 */
open class People private constructor(var id: String, var name: String) {
    //能够类中初始化属性:
    var customName = name.toUpperCase() //初始化属性
    //次构造函数,使用constructor前缀声明,且必须调用primary constructor,使用this关键字
    constructor( id: String, name: String, age: Int) : this(id, name) {
        println("constructor")
    }
    init {
        println("初始化操作,可使用constructor參数")
    }
    //须要open修饰,子类才干够
    open fun study() {
        print("study")
    }
    //People前的冒号":"是继承的意思。实现类接口的时候也是冒号
class Student(id: String, name: String) : People(id, name) {
    var test: Number = 3
    private var name1: String?
        get() {
            return name1
        }
        set(value) {
            name1 = value
        }
  //override修饰的方法,默认是能够被继承的。若希望不被继承。能够使用 final 关键词修饰
    override fun study() {
        super.study()
    }
}

}

数据类用来保存数据。相似于POJO类。使用data关键词进行定义,编译器默认会为数据类生成以下四个方法

  • equals()
  • hashCode()
  • toString()
  • copy()
    通过数据类你会看到Kotlin的简洁性。我们创建一个Staff类,有String类型的name。position和泛型T(使用泛型仅仅是为了在Kotlin中接触以下泛型)
    java实现代码:
public class StaffJ<T> {
    private String name;
    private String position;
    private T age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }
    public T getAge() {
        return age;
    }
    public void setAge(T age) {
        this.age = age;
    }
}

Kotlin数据类:

data class Staff<T>(var name: String,  val position: String,var age:T)

通过对照我们就看出了长处了。一行代码就实现了,详细使用

 var staff = Staff("code4Android","Androidproject师","22")  //实例化对象

要获取某个属性如获取名字staff.name,赋值就是staff.name=”code4Android2”,既然说了这样能够赋值,可是动手的小伙伴说为什么我敲的报错啊,例如以下

    staff.position="前端"

编译报错了。在前面我们说过val修饰的属性仅仅能赋值一次,那在这里val修饰的属性我们是不能再次赋值的。

fun main(arg:Array<String>){
    var staff = Staff("code4Android","Androidproject师","22")  //实例化对象
    staff.name="code4Android2"
    var staff1=staff.copy()
    //使用copy的时候能够指定默认值,能够指定随意个用逗号","隔开
    var staff2=staff.copy(name="ccc",position = "kotlin")
    println("name:${staff2.name} position:${staff2.position} age ${staff2.age}")
    //staff.position="Kotiln" //val不能再次赋值
    var anotherStaff= Staff("code4Android","Androidproject师",22) //实例化对象

    println("staff toString(): ${staff.toString()} anotherStaff toString(): ${anotherStaff.toString()}")
    println("staff hashCode(): ${staff.hashCode()} anotherStaff hashCode(): ${anotherStaff.hashCode()}")
    println("staff is equals another staff ?

${staff.equals(anotherStaff)}") }

上面使用了字符模板,在Kotlin中有两种字符模板形式。$<变量>、${<变量>}

    var name:String="Code4Android"
    println("我的名字叫$name")
    println("我的名字叫${name}")
/**
 * java同意使用匿名内部类;kotlin也有相似的概念,称为对象表达式-object expressions
 */
open class KeyBord{
    open fun onKeyEvent(code:Int):Unit = Unit
}

/**匿名内部类**/
var key=object :KeyBord(){
    override open fun onKeyEvent(code:Int):Unit = Unit
}

枚举

enum class Color{
    RED,BLACK,BLUE,GREEN,WHITE
}
fun display(){
    var color:Color=Color.BLACK
    Color.valueOf("BLACK") // 转换指定name为枚举值,若未匹配成功,会抛出IllegalArgumentException
    Color.values() //已数组的形式。返回枚举值
    println(color.name)////获取枚举名称
    println(color.ordinal)//获取枚举值在全部枚举数组中定义的顺序,0開始
}

在Kotlin中枚举还支持方法。

扩展

/**
 * fun receiverType.functionName(params){
 *body
 *}
 * receiverType : 待扩展的类名
 * .            :修饰符为扩展符
 * functionName :为自己定义的扩展函数名,
 * params       :为自己定义的扩展函数參数。可为空
 * 扩展函数作用域。受函数的visibility modifiers影响
 * 扩展函数并没有对原类做改动,而是为被扩展类的对象加入新的函数。
 * 有一条规则。若扩展函数和类原有函数一致,则使用该函数时。会优先使用类本身的函数。
 */
class Employee(var name: String) {
    fun print() {
        println("Employee")
    }
}

//扩展函数
fun Employee.println() {
    print("println:Employee name is $name")
}


/**
 * 能够扩展一个空对象
 */
fun Any?.toString1(): String {
    if (this == null)
        return "toString1:null"
    else {
        return "toString1" + toString()
    }
}

/**
 * 扩展属性
 * 因为扩展属性实际上不会向类加入新的成员,
 * 因此无法让一个扩展属性拥有一个后端域变量. 所以,对于扩展属性不同意存在初始化器.
 * 扩展属性的行为仅仅能通过明白给定的取值方法与设值方法来定义,也就意味着扩展属性仅仅
 * 能被声明为val而不能被声明为var.假设强制声明为var,即使进行了初始化。
 * 在执行也会报异常错误,提示该属性没有后端域变量。

*/ val Employee.lastName: String get() { return "get:"+name }

使用

fun main(arg: Array<String>) {
    var employee = Employee("Code4Android")
    employee.print()
    employee.println()
    println(employee.toString1())
    println(employee.lastName)
}

代理

**
 * 被代理接口
 */
interface Base {
    fun display()
}

/**
 * 被代理类
 */
open class BaseImpl : Base {
    override fun display() = print("just display me.")
}

/**
 * 代理类。使用:以及关键词by进行声明
 * 许代理属性和其它继承属性共用
 * class Drived(base: Base) : Base by base,BaseImpl()
 */
class Drived(base: Base) : Base by base

//使用
fun show() {
    var b = BaseImpl()
    var drived = Drived(b)
    drived.display()

}

**
 * 代理类型:
 * 懒载入:Lazy
 * 观察者:Delegates.observable()
 * 非空属性:Delegates.notNull<>()
 */
class Water {

    public var weight:Int by Delegates.notNull()
    /**
     * 代理属性
     */
    public val name: String by lazy {
        println("Lazy.......")
        "Code4Android"
    }
    public var value: String by Delegates.observable("init value") {
        d, old, new ->
        println("$d-->$old->$new")
    }
}

fun main(arg: Array<String>) {
    show()
    var water = Water()
    println(water.name)
    println(water.name)
    water.value = "11111"
    water.value = "22222"
    water.value = "33333"
    println(water.value)
    println(water.value)
    //必须先赋值否则IllegalStateException: Property weight should be initialized before get.
    water.weight=2
    print(water.weight)
}

操作符::

val String.lastChar: Char
    get() = this[this.length - 1]

class A(val p: Int)
   //1,反射得到执行时的类引用:
    val c = Student::class
    //2,函数引用
    fun isOdd(x: Int) = x % 2 != 0
    val numbers = listOf(1, 2, 3)
    println(numbers.filter(::isOdd)) 

    //3,属性引用(在此引用main函数主体外声明的变量)
    println(::x.get())
    ::x.set(2)
    println(x)
    //4。::x 表达式评估为 KProperty<Int> 类型的属性,它同意我们使用 get() 读它的值或者使用名字取回它的属性
    val prop = A::p
    println(prop.get(A(1))) 

    //5,对于扩展属性
    println(String::lastChar.get("abc")) 

    //6,与 java 反射调用
    println(A::p.javaClass),
    var f: Array<Field> = A::p.javaClass.declaredFields

伴生对象

伴生对象(companion object )相似于java中的静态关键字static。

在Kotlin没有这个关键字,而是伴生对象,详细使用方法

open class People constructor(var id: String,  var name: String){
    //能够类中初始化属性:
    var customName = name.toUpperCase() //初始化属性

    //使用constructor前缀声明,且必须调用primary constructor,使用this关键字
    constructor( id: String, name: String, age: Int) : this(id, name) {
        println("constructor")
    }

    init {
        println("初始化操作,可使用constructor參数")
    }
   //,Kotlin的class并不支持static变量,所以须要使用companion object来声明static变量,
   // 事实上这个platformStatic变量也不是真正的static变量,而是一个伴生对象,
    companion object {
        val ID = 1
    }
}

使用的话直接People.ID。

单例模式

在Kotlin中使用object修饰类的时候,。该类是单例对象。

/**
 * 使用object定义类,该类的实例即为单例,訪问单例直接使用类名,不能通过构造函数进行訪问,不同意有构造函数
 * Place.doSomething() // 訪问单例对象
 */
object Singleton {
    fun doSomething() {
        println("doSomething")
    }
}


/**
 * 实例化的时候,单例是懒载入。当使用的时候才去载入;而对象表达式是在初始化的地方去载入。

* * 当在类内部使用 object 关键词定义对象时,同意直接通过外部类的类名訪问内部对象进而訪问其相关属性和方法。相当于静态变量 * 能够使用companion修饰单例,则訪问其属性或方法时,同意省略单例名 * MyClass.doSomething() // 訪问内部单例对象方法 */ class MyClass { companion object Singleton { fun doSomething() { println("doSomething") } } }

好了,今天就介绍到这里,文中若有错误欢迎指出,Have a wonderful day.

Kotlin英文官网
Kotlin学习中文官网
在线学习演示样例

以上是关于Kotlin初探的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin初探

Kotlin初探

安卓官方开发语言:Kotlin 初探

OkHttp初探2:如何使用OkHttp进行下载封装?带进度条?Kotlin+Flow版本。

如何从片段 KOTLIN 中调用意图 [重复]

Kotlin基础初探