分享

Kotlin 新手别慌,可先了解这些入门知识

 剑客阿良 2017-05-19


昨天推送《Kotlin 是安卓官方团队支持的新语言》资讯后,有位朋友问「普通安卓程序员不就回失业?」


如果你是才知道 Kotlin,请先了解下面这些


  • Kotlin 是一个基于 JVM 的新编程语言,用 JetBrains 的话来说是「更现代化、更强大」的语言。


  • Kotlin 由 JetBrains 在 2010 年开发,2011 年开源了: https://github.com/JetBrains/kotlin  。2016 年发布 1.0 版,2017 年 4 月发布 1.1.2 版。


  • 谷歌安卓团队和 JetBrains 的关系不一般。Android Studio 就是基于 JetBrains 开发的 IntelliJ IDEA 社区版; 


  • 谷歌和 JetBrains 将为 Kotlin 成立一个非盈利基金会。Kotlin 语言的开发,还是 JB 为主导。

  • Kotlin 语言的开发团队,目前大约 40 人。会继续往常那样运作。Andrey Breslav 还是 Kotlin 语言的首席设计师。


  • JetBrains 对 Kotlin 的愿景是:用同一种语言,桥接多平台的不同应用的端对端开发。包括全栈 Web 应用、Android 和 iOS 客户端、嵌入式/物联网等等。


  • Kotlin 与 Java 100% 互通,并具备诸多 Java 尚不支持的新特性。


  • Android Studio 3.0 预览版已支持 Kotlin。下载 AS 3.0 预览版后,打开现有的 Java 文件,在「Code」菜单中选择「Convert Java File to Kotlin File」。AS 会添加 Kotlin 依赖,然后把 Java 代码转成同等功能的 Kotlin 代码。

  • Kotlin 易于上手,可以逐步引入到现有项目中。这意味着安卓开发者目前现有的技能和技术积累/投资并没有浪费。


昨天也在评论中回应「茶余饭后」,后续会推送一些 Kotlin 技术文章,那今天就推荐我们专栏作者在 4 月 26 日写的:


《Kotlin 初探》


作者:伯乐在线专栏作者 - Code4Android

如有好文章投稿,请点击 → 这里了解详情


前言


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(savedInstanceStateBundle?) {

        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(menuMenu)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(itemMenuItem)Boolean {

        val id = item.itemId

        if (id == R.id.action_settings) {

            return true

        }

        return super.onOptionsItemSelected(item)

    }

}

 

 

object Test {

    private var strString? = null

 

    @JvmStatic fun main(argsArray) {

        str = 'Code4Android'

        println(str)

    }

}


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


Hello World!


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



package com.learnrecord

 

/**

*Created by Code4Android on 2017/4/21.

*/

 

var strString = ''

 

fun main(argsArray) {

    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 iArrayIntArray = intArrayOf(1, 2, 3)

val sArrayArrayString> = ArrayString>(3, { i -> i.toString() })

val anyArrayArrayAny> = arrayOf(1, '2', 3.0, 4.1f) // 可将类型进行混排放入同一个数组中

val lArrayLongArray = longArrayOf(1L, 2L, 3L)


函数


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


    fun sum(aInt, bInt)Int {

        return a + b

    }


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


 fun sum(aInt, bInt) = 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(aInt)Unit {

        println(a)

    }


Unit就类似我们java中的void,即没有返回值,此时我们可以省略


    fun printInt(aInt) {

        println(a)

    }


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


  • abstract //抽象类标示

  • final //标示类不可继承,默认属性

  • enum //标示类为枚举

  • open //类可继承,类默认是final的

  • annotation //注解类

  • private //仅在同一个文件中可见

  • protected //同一个文件中或子类可见,不可修饰类

  • public //所有调用的地方都可见

  • internal //同一个模块中可见,若类不加修饰符,则默认为该修饰符,作用域为同一个应用的所有模块,起保护作用,防止模块外被调用。


操作符


直接上代码如下


  //操作符 shl 下面对Int和Long

var aInt = 4

var shlInt = a shl (1) //Java中的左移运算符

var shrInt = a shr (1) //Java中的右移运算符 >>

var ushrInt = a ushr (3) //无符号右移,高位补0 >>>

var andInt = 2 and (4) //按位与操作 &

var orInt = 2 or (4) //按位或操作 |

var xorInt = 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(arrayArray) {

        //第一种方式直接输出字符(类似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<>

        var i = 0

        while (i <>array.size) {

            println(array[i++])

        }

    }


使用when判断类型


fun whenFun(objAny) {

        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<>

    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 idString, var nameString) {

    //可以类中初始化属性:

    var customName = name.toUpperCase() //初始化属性

    //次构造函数,使用constructor前缀声明,且必须调用primary constructor,使用this关键字

    constructor( idString, nameString, ageInt) : this(id, name) {

        println('constructor')

    }

    init {

        println('初始化操作,可使用constructor参数')

    }

    //需要open修饰,子类才可以

    open fun study() {

        print('study')

    }

    //People前的冒号':'是继承的意思,实现类接口的时候也是冒号

class Student(idString, nameString) : People(id, name) {

    var testNumber = 3

    private var name1String?

        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 {

    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 StaffT>(var nameString, val positionString,var age:T)


通过对比我们就看出了优点了,一行代码就实现了,具体使用


var staff = Staff('code4Android','Android工程师','22')  //实例化对象


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


staff.position='前端'


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


fun main(arg:Array){

    var staff = Staff('code4Android','Android工程师','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 anotherStaffStaff('code4Android','Android工程师',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 nameString) {

    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.lastNameString

    get() {

        return 'get:'+name

    }


使用


fun main(argArray) {

    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(baseBase) : 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 nameString by lazy {

        println('Lazy.......')

        'Code4Android'

    }

    public var valueString by Delegates.observable('init value') {

        d, old, new ->

        println('$d-->$old->$new')

    }

}

 

fun main(argArray) {

    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.lastCharChar

    get() = this[this.length - 1]

 

class A(val pInt)


//1,反射得到运行时的类引用:

    val c = Student::class

    //2,函数引用

    fun isOdd(xInt) = 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 类型的属性,它允许我们使用 get() 读它的值或者使用名字取回它的属性

    val prop = A::p

    println(prop.get(A(1)))

 

    //5,对于扩展属性

    println(String::lastChar.get('abc'))

 

    //6,与 java 反射调用

    println(A::p.javaClass),

    var fArray = A::p.javaClass.declaredFields


伴生对象


伴生对象(companion object )类似于java中的静态关键字static。在Kotlin没有这个关键字,而是伴生对象,具体用法


open class People constructor(var idString,  var nameString){

    //可以类中初始化属性:

    var customName = name.toUpperCase() //初始化属性

 

    //使用constructor前缀声明,且必须调用primary constructor,使用this关键字

    constructor( idString, nameString, ageInt) : 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学习中文官网:/


  • 在线学习示例http://try./#/Examples/Hello,%20world!/Simplest%20version/Simplest%20version.kt


  • 示例源码传送门

    https://github.com/xiehui999/KotlinForAndroid


看完本文有收获?请分享给更多人

 关注「安卓开发精选」,提升安卓开发技术

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多