kotlin_kotlin- - CSDN
kotlin 订阅
Kotlin (科特林)是一个用于现代多平台应用的静态编程语言 [1]  ,由 JetBrains 开发。Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。除此之外Kotlin还可以编译成二进制代码直接运行在机器上(例如嵌入式设备或 iOS)。 [1]  Kotlin已正式成为Android官方支持开发语言。 展开全文
Kotlin (科特林)是一个用于现代多平台应用的静态编程语言 [1]  ,由 JetBrains 开发。Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。除此之外Kotlin还可以编译成二进制代码直接运行在机器上(例如嵌入式设备或 iOS)。 [1]  Kotlin已正式成为Android官方支持开发语言。
信息
发布时间
2016年2月15日
语言版本
1.3.72
开发公司
JetBrains
推出时间
2011年7月
中文名
科特林
性    质
编程语言
外文名
Kotlin
开源协议
Apache 2
Kotlin简介
2011年7月,JetBrains推出baiKotlin项目,这是一个面向duJVM的新语言 [2]  ,它已被开发一年之久。JetBrains负责人Dmitry Jemerov说,大多数语言没有他们正在寻找的特性,Scala除外。但是,他指出了Scala的编译时间慢这一明显缺陷。Kotlin的既定目标之一是像Java一样快速编译。 [1]  2012年2月,JetBrains以Apache 2许可证开源此项目。 [2]  Jetbrains希望这个新语言能够推动IntelliJ IDEA的销售。 [3]  Kotlin v1.0于2016年2月15日发布。这被认为是第一个官方稳定版本,并且JetBrains已准备从该版本开始的长期向后兼容性。 [2]  在Google I/O 2017中,Google宣布在Android上为Kotlin提供一等支持。 [2] 
收起全文
  • 第一阶段:Kotlin的基本语法,包括类和方法的定义及调用、数组、条件判断、循环控制、接口定义及实现、泛型等等; 第二阶段:用Kotlin创建Android项目,运用kotlin初始化界面、控件、网络请求及数据处理并展示等
  • Kotlin基础与Android实战

    2020-01-08 10:24:43
    - 带你学习Google强推的新静态语言Kotlin - 通俗易懂的囊括Kotlin语言基础 - 通过所学的Kotlin进行Android开发 - 所使用的的技术点  - Kotlin  - Android项目实战  - Retrofit网络请求框架
  • 征服Kotlin视频教程

    2018-11-29 17:30:09
    该课程包括Kotlin开发环境搭建、Kotlin基础知识、类和接口、枚举类、扩展、泛型、函数、lambdas表达式、对象、标准API等。
  • 随着Kotlin的推广,一些国内公司的安卓项目开发,已经从Java完全切成Kotlin了。虽然Kotlin在各类编程语言中的排名比较靠后(据TIOBE发布了 19 年 8 月份的编程语言排行榜,Kotlin竟然排名45位),但是作为安卓开发者...
  • Kotlin初探

    2017-04-26 17:56:42
    示例源码传送门前言Kotlin是一种在 Java虚拟机上执行的静态型别编程语言,它主要是由俄罗斯圣彼得堡的JetBrains开发团队所发展出来的编程语言。该语言有几个优势 1. 简洁 它大大减少你需要写的样板代码的数量。 ...

    示例源码传送门

    前言

    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的基本语法。相信很容易上手。

    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","Android工程师","22")  //实例化对象

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

        staff.position="前端"

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

    fun main(arg:Array<String>){
        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 anotherStaff= Staff("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 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语言包含了Java语言中的所有运算符的特性,并结合C语言的优点,增加自定义运算符的逻辑。这些运算符之中,主要

    运算符

    计算机程序中最小的程序单位成为表达式,每个表达式都可以由两部分组成,即操作数和运算符。操作数可以是变量、常量、类、数组、方法等,甚至是其他表达式。而运算符则用于支出表达式中单个或者多个操作数参与运算的规则,表达式通过运算之后产生的值依赖于表达式中包含的运算符的优先级和结核性。Kotlin语言包含了Java语言中的所有运算符的特性,并结合C语言的优点,增加自定义运算符的逻辑。这些运算符之中,主要包括有:算数运算符、区间运算符、逻辑运算符、关系运算符、赋值运算符、自增自减运算符等。

    根据操作数的数量来划分,运算符又可以分为一目运算符、双目运算符。
    - 一目运算符用于单一操作对象,又称单目运算符,如:++a、!b、i–等。
    - 双目运算符是中置的,它拥有两个操作数,比如:a+3、a*b

    Kotlin中没有三目运算符

    基础运算符

    基础运算符中包含了我们在编码工程中常用的一系列运算符,使我们编写程序的基本组成部分,了解基础运算符的用法可以尽可能的避免一些语法和逻辑上的基础性错误。

    赋值运算符(=)

    赋值运算a=b,表示等号右边的b初始化或者维护等号左边的a,b可以是变量、常量、字面量或表达式,如:

    var IntA:Int = 5
    val IntB:Int = 10
    
    IntA = 2 + 1;
    IntA = IntB

    在Kotlin语言中还有另一种赋值运算符,叫做算术自反赋值运算符。它是一种由两个普通运算符组成的符合运算符,它包括:“+=”、“-=”、“*=”、“/=”、“%=”。使用方式如下:

    var IntA:Int = 5
    val IntB:Int = 10
    
    IntA += IntB // 作用等于 IntA = IntA + IntB
    IntA -= IntB // 作用等于 IntA = IntA - IntB
    IntA *= IntB // 作用等于 IntA = IntA * IntB
    IntA /= IntB // 作用等于 IntA = IntA / IntB
    IntA %= IntB // 作用等于 IntA = IntA % IntB

    算数运算符

    算术运算符用于数值类型的运算,Kotlin语言支持基本的算术运算:加法“+”、减法“-”、乘法“*”、除法“/”、取余“%”、以及自增自减运算,如:

    var IntA:Int = 5 + 5  // 10
    val IntB:Int = 10 - 2 // 8
    val IntC:Int = 3 * 4  // 12
    val IntD:Int = 10 / 5 // 2
    val IntE:Int = 10 % 3 // 1,除不尽,保留余数
    val IntF:Int = 10 / 6 // 1,除不尽,仅保留整数部分
    
    IntA = IntA / 0 // 报错,除数不能为0

    自增自减运算符(++、–)

    自增和自减运算符也是单目运算符,因为它只有一个操作数。自增运算符 “++” 表示使操作数加1,自减运算符 “–” 表示使操作数减1,其操作数可以使整数和浮点型等数字类型,如:

    var intA : Int = 5
    
    intA++ // 等于 intA = intA + 1
    println("intA = " + intA)  // 输出 intA = 6

    值得注意的是,自增运算符和自减运算符还会分为前置自增、后置自增、前置自减和后置自减,放在操作数前面的是前置,放在操作数后面的是后置运算符。

    后置运算,则为先进性表达式返回,才进行自增、自减运算。前置运算符,则先进行自增、自减运算,在进行表达式返回。如:

    var intIncA: Int = 5
    var intIncB: Int = 5
    var intIncC: Int = 5
    var intIncD: Int = 5
    
    println(++intIncA) // 先自增, 后返回。 输出 :6
    println(--intIncB) // 先自减, 后返回。 输出 :4
    println(intIncC--) // 先返回, 后自减。 输出 :5
    println(intIncD++) // 先返回, 后自增。 输出 :5

    字符串连接符(+)

    两个字符串可以连接在一起成为一个新字符串,这种操作被成为字符串连接,在Kotlin语言中连接字符串可以用 “+”。如:

    "hello " + "world" // 等于 "hello world"

    字符串连接操作两边都是字符串,而很多情况下我们使用连接符仅有一侧是字符串,另一侧是其他类型。这个时候,系统则会自动调用toString方法转化为字符串,进行拼接。这个时候则调用则是String重载的plus方法,后面我们会具体介绍运算符重载,Kotlin中String的源码如下:

    image

    故此,进行字符串与其他类型拼接我们都将String类型的操作符至于连接符 “+” 左侧。

    var intA : Int = 1
    var StringA : String = "String Head "
    
    println(intA + StringA) // 报错,调用的是Int.plus方法
    println(StringA + intA) // 输入内容:String Head 1

    关系运算符

    关系运算符是指:使用关系运算符对两个操作数或表达式进行运算,产生的结果为真或者假。

    运算符 名称 示例 功能 缩写
    < 小于 a
    println(10 == 10) // true
    println(1 != 5)   // true
    println(1 < 5)    // true
    println(1 > 5)    // false
    println(4 <= 5)   // true
    println(4 >= 5)   // false

    注意:
    1. 关系运算符的优先级低于算术运算符。
    2. 关系运算符的优先级高于赋值运算符。

    区间运算符(a..b)

    区间运算符,顾名思义就是可以用来表示两个操作数之间的范围集合。a..b也就我们平时所说的,从a到b所有的数字集合。在Kotlin语言之中,有两种区间运算符:闭区间运算符和开区间运算符。
    - 闭区间运算符 : “a..b”从a到b范围内所有的值,包括a和b。
    - 半闭区间运算符 : “a until b”从a到b范围内所有的值,包括a和不包括b。

    区间表达式由具有操作符形式 “..”rangeTo 辅以 in!in 而得。区间是为任何可比较类型定义的,但对于整型原生类型,它有一个优化的实现。以下是使用区间的一些示例:

    for (i in 1..10) { // 等同于 1 <= i && i <= 10
        println(i)
    }
    
    for (i in 1.rangeTo(10)) {  // 等同于 1 <= i && i <= 10
        println(i)
    }
    
    for (i in 'a'..'z') { // 等同于 'a' <= i && i <= 'z'
        println(i)
    }

    in 代表在区间内,!in表示不在。

    整型区间有一个额外的特性:它们可以迭代。 Kotlin编译器负责将其转换为类似 Java 的基于索引的 for循环而无额外开销。

    for (i in 1..4) print(i) // 输出“1234”
    
    for (i in 4..1) print(i) // 什么都不输出

    运行上述例子,我们可以发现如果只写 “..”,这个区间值只是顺序。如果你想倒序迭代数字呢?也很简单。你可以使用标准库中定义的 downTo 方法:

    for (i in 4 downTo 1) print(i) // 输出“4321”

    能否以不等于 1 的任意步长迭代数字? 当然没问题, 使用step方法就可以做到:

    for (i in 1..4 step 2) print(i) // 输出“13”
    
    for (i in 4 downTo 1 step 2) print(i) // 输出“42”

    那么我们如何创建一个半闭区间呢?可以使用 until方法 :

    for (i in 1 until 10) {   // i in [1, 10) 排除了 10
         println(i)
    }

    逻辑运算符

    逻辑运算使用等式表示判断,把推理看做等式运算,这种变换的有效性不依赖人们对符号的解释,只依赖符号组合的变换。Kotlin语言和Java一样,支持三个标准逻辑运算符,逻辑与、逻辑或、逻辑非。

    • && : 逻辑与,可以理解为并且的意思.
    • || : 逻辑或,可以理解为或者的意思,也就是条件可以二取一
    • : 逻辑非,取反

    逻辑运算表达式中,操作数值的组合不同,整个表达式的值也不同。在这里我们给出一个逻辑运算的值搭配总结表:

    a b a&&b a||b !a
    false false false false true
    true false false true false
    false true false true true
    true true true true false

    空安全操作符(?、!!)

    在Java开发的过程中遇到的最多的异常就是NullPointException(NPE),空异常的问题很多是不可预见的。一直以来,NullPointException空指针异常在开发中是最低级也最致命的问题。我们往往需要进行各种null的判断以试图去避免NPE的发生。在Kotlin语言中一切皆对象,出现NPE则是致命性的问题。所提,在Kotlin语言中提出了预先判空处理,为此引用了两个操作符:判空操作符“?”强校验“!!”操作符

    预定义,是否能容纳空(?)

    在Kotlin中,类型系统区分一个引用可以容纳null,还是不能容纳null。 Kotlin中绝大部分的对象都是不能够容纳null的,例如,基础类型中的常规变量不能容纳null:

    var a: String = "abc"
    a = null // 编译错误

    如果要允许为null,我们可以声明一个变量为可空字符串,需要在定义的类型后面紧跟一个问号 “?”,如上述例子则写作* String?*

    var b: String? = "abc"
    b = null // 这样编译没问题

    对于无法容纳null的类型,我们可以放心的对它的属性进行调用。

    var a: String = "abc"
    var aLength = a.length // 放心调用,a肯定不会为null

    同样的操作,我们则不能够对b字符串进行操作,对于可能为空的类型进行操作,我们就必须判空。

    判空(?)

    在Kotlin语言中判断一个对象是否为空有两种方式,第一种就是如同Java语言一样,使用if-else进行判空;另一中就还是使用操作符 “?” 进行判断。

    // 在Java语言中我们使用的判空方法。
    if (b != null && b.length > 0) {
        println("String of length ${b.length}")
    } else {
        println("Empty string")
    }
    
    // Kotlin,可空类型的判断
    println("String of length ${b?.length}")

    咋一看,差别不是很大,但,null安全,在链式调用中很有用。例如,如果一个员工 Bob 可能会(或者不会)分配给一个部门, 并且可能有另外一个员工是该部门的负责人,那么获取 Bob 所在部门负责人(如果有的话)的名字,我们写作:

    bob?.department?.head?.name

    如果任意一个属性(环节)为空,这个链式调用就会返回 null。如果要只对非空值执行某个操作,安全调用操作符可以与 let 一起使用:

    val listWithNulls: List<String?> = listOf("A", null)
    for (item in listWithNulls) {
         item?.let { println(it) } // 输出 A 并忽略 null
    }

    !! 操作符

    很多情况下,NullPointerException对我们来说还是有一定意义的,我们必须catch住此异常。那么,Kotlin中的又有空安全的机制存在,我们就必须对null进行强校验。这里,Kotlin给我们提供的操作符为两个引号 “!!”,如:

    var a : String? = null // 必须是可空类型,不然强校验没有意义
    val lenC = a!!.length // 这样做就会报错

    如果,希望强校验希望系统抛出一个NullPointerException,那么必须让定义的变量可容纳null,不然强校验就失去意义了。

    安全的类型转换

    如果对象不是目标类型,那么常规类型转换可能会导致 ClassCastException。 另一个选择是使用安全的类型转换,如果尝试转换不成功则返回 null:

    val aInt: Int? = a as? Int

    可空类型的集合

    如果你有一个可空类型元素的集合,并且想要过滤非空元素,你可以使用 filterNotNull 方法来实现。

    val nullableList: List<Int?> = listOf(1, 2, null, 4)
    val intList: List<Int> = nullableList.filterNotNull()

    Elvis 操作符(?:)

    Elvis操作符很像是Java语言中的三目表达式,然而由于三目表达式的对于很多开发者来说都比较难懂,导致经常用错。Kotlin对三目表达式进行了升级,即elvis表达式的来源,Kotlin中不再支持三目表达式。Elvis操作符的用法如下:

    <结果> = <表达式1> ?: <表达式2>

    如果表达式1为null,则返回表达式2的内容,否则返回表达式1。请注意,当且仅当左侧表达式1为空时,才会对右侧表达式求值。如:

    // Elvis操作符获取b字符串的长度,如果b为null则返回-1
    val lenB = b?.length ?: -1
    
    // 等同于逻辑
    val lenA: Int = if (b != null) {
        b.length
    } else {
        -1
    }

    位运算符

    运算符优先级

    运算符的优先级使得一些运算符优先于其他运算符,从而是的高优先级的运算符会先被计算。而运算符的结合性用于定义相同优先级的运算符在一起的时和表达式结合或关联规则,在混合表达式中,运算符的优先级和结合性是非常重要的。如:

    2 + 3 - 4 * 5 // 等于 -15

    如果严格地从左到右,计算过程会是这样:

    2 + 3 = 5
    5 - 4 = 1
    1 * 5 = 5

    但实际上乘法优先于减法被计算,所以实际上的运算过程是:

    2 + 3 = 5
    4 * 5 = 20
    5 - 20 = -15

    这类似的情况和我们在数学中一样:右括号先算括号里的, 然后先乘除后加减。在Kotlin语言中也拥有自己运算符的优先级别和结合性。这里我们把所有的运算符总结为下表:

    优先级 运算符 结核性
    1 ()、[] 从左到右
    2 !、+(正)、-(负)、~、++、– 从右到左
    3 *、/、% 从左到右
    4 +(加)、-(减) 从左到右
    5 <<、>>、>>> 从左到右
    6 <、<=、>、>= 从左到右
    7 ==、!= 从左到右
    8 &(按位与) 从左到右
    9 ^ 从左到右
    10 | 从左到右
    11 && 从左到右
    12 || 从左到右
    13 ?: 从右到左
    14 =、+=、-=、/=、%=、&=、|=、^=、~=、<<=、>>=、>>>= 从右到左

    运算符重载

    预定义的运算符的操作对象只能是基本数据类型,实际上,对于很多我们自定义的对象也需要有类似的运算操作。运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据导致不同类型的行为。

    运算符重载是自C++语言器就支持的特性,然而在Java语言之中这个特性就不在支持,在很多高级科学运算上很不方便,Kotlin语言又从新支持此特性。不同于符合赋值运算符,我们可以定义/、==、-、+、*、%、<<、>>、!、&、|、^等运算符的逻辑功能。重载一个运算符,我们必须重写它所对应的operator方法,如下就是一个针对 “+” 运算符的定义:

    public operator fun plus(other: Byte): Float

    这里,我们可以看得出来运算符重载的实质是方法的重载。在实现过程中,首先把指定的运算表达式转化为对运算方法的调用,运算对象转化为运算符方法的实参,然后根据实参的类型来确定需要调用达标函数,之后Kotlin会将对应的符号运算切换到方法之中。如Float类型针对 “+” 运算符所定义的:
    image

    重载一元运算符

    一元前缀操作符

    表达式 转换方法
    +a a.unaryPlus()
    -a a.unaryMinus()
    !a a.not()

    这个表是说,当编译器处理例如表达式 +a 时,它执行以下步骤:
    1. 确定 a 的类型,令其为 T。
    2. 为接收者 T 查找一个带有 operator 修饰符的无参函数 unaryPlus(),即成员函数或扩展函数。
    3. 如果函数不存在或不明确,则导致编译错误。
    4. 如果函数存在且其返回类型为 R,那就表达式 +a 具有类型 R。

    递增和递减

    表达式 转换方法
    a++ a.inc()
    a– a.dec()

    inc() 和 dec() 函数必须返回一个值,它用于赋值给使用 ++ 或 – 操作的变量。它们不应该改变在其上调用 inc() 或 dec() 的对象。

    编译器执行以下步骤来解析后缀形式的操作符,例如 a++:
    1. 确定 a 的类型,令其为 T。
    2. 查找一个适用于类型为 T 的接收者的、带有 operator 修饰符的无参数函数 inc()。
    3. 检查函数的返回类型是 T 的子类型。

    计算表达式的步骤是:
    1. 把 a 的初始值存储到临时存储 a0 中,
    2. 把 a.inc() 结果赋值给 a,
    3. 把 a0 作为表达式的结果返回。
    4. 对于 a–,步骤是完全类似的。

    对于前缀形式 ++a 和 –a 以相同方式解析,其步骤是:
    1. 把 a.inc() 结果赋值给 a,
    2. 把 a 的新值作为表达式结果返回。

    二元操作符

    算术运算符

    表达式 转换方法
    a + b a.plus(b)
    a - b a.minus(b)
    a * b a.times(b)
    a / b a.div(b)
    a % b a.mod(b)
    a..b a.rangeTo(b)

    对于此表中的操作,编译器只是解析成翻译为列中的表达式。

    请注意,自 Kotlin 1.1 起支持 rem 运算符。Kotlin 1.0 使用 mod 运算符,它在 Kotlin 1.1 中被弃用。

    “In”操作符

    表达式 转换方法
    a in b b.contains(a)
    a !in b !b.contains(a)

    对于 in 和 !in,过程是相同的,但是参数的顺序是相反的。

    索引访问操作符

    表达式 转换方法
    a[i] a.get(i)
    a[i, j] a.get(i, j)
    a[i_1, …, i_n] a.get(i_1, … , i_n)
    a[i]=b a.set(i, b)
    a[i,j]=b a.set(i, j, b)
    a[i_1, … , i_n]=b a.set(i_1,… ,o_n,b)

    方括号转换为调用带有适当数量参数的 get 和 set。

    调用操作符

    表达式 转换方法
    a(i) a.invoke(i)
    a(i, j) a.invoke(i, j)
    a(i_1, … , i_n) a.invoke(i_1, …, i_n)

    圆括号转换为调用带有适当数量参数的 invoke。

    广义赋值

    表达式 转换方法
    a += b a.plusAssign(b)
    a -= b a.minusAssign(b)
    a *= b a.timesAssign(b)
    a /= b a.divAssign(b)
    a %= b a.modAssign(b)

    对于赋值操作,例如 a += b,编译器执行以下步骤:
    1. 如果右列的函数可用
    2. 如果相应的二元函数(即 plusAssign() 对应于 plus())也可用,那么报告错误(模糊)。
    3. 确保其返回类型是 Unit,否则报告错误。
    4. 生成 a.plusAssign(b) 的代码
    5. 否则试着生成 a = a + b 的代码(这里包含类型检查:a + b 的类型必须是 a 的子类型)。

    注意:赋值在 Kotlin 中不是表达式。

    相等与不等操作符

    表达式 转换方法
    a == b a?.equals(b) ?: (b === null)
    a != b !(a?.equals(b) ?: (b === null))

    这个 == 操作符有些特殊:它被翻译成一个复杂的表达式,用于筛选 null 值。 null == null 总是 true,对于非空的 x,x == null 总是 false 而不会调用 x.equals()。

    注意:=== 和 !==(同一性检查)不可重载。

    比较操作符

    表达式 转换方法
    a > b a.compareTo(b) > 0
    a < b a.compareTo(b) < 0
    a >= b a.compareTo(b) >= 0
    a <= b a.compareTo(b) <= 0

    所有的比较都转换为对 compareTo 的调用,这个函数需要返回 Int 值。

    位运算

    对于位运算,Kotlin 并没有提供特殊的操作符, 只是提供了可以叫中缀形式的方法, 比如:

    // 给 Int 定义扩展
    infix fun Int.shl(x: Int): Int {
    ……
    }
    
    // 用中缀表示法调用扩展方法
    1 shl 2
    
    // 等同于这样
    1.shl(2)

    下面是全部的位运算操作符(只可以用在 Int 和 Long 类型):

    表达式 转换方法
    shl(bits) 有符号左移 (相当于Java <<)
    shr(bits) 有符号右移 (相当于Java >>)
    ushr(bits) 无符号右移(相当于Java >>>)
    and(bits) 按位与
    or(bits) 按位或
    xor(bits) 按位异或
    inv() 按位取反

    展开全文
  • kotlin的?.和!!.

    2018-04-21 13:16:51
    ?.表示当前对象如果为空则不执行, !!.表示当前对象如果为空也执行,然后会抛出空异常 一个?.的例子: 一个!!.的例子: 参考: Kotlin中问号(?)和两个叹号(!!) ...

    ?.表示当前对象如果为空则不执行,
    !!.表示当前对象如果为空也执行,然后会抛出空异常

    一个?.的例子:
    这里写图片描述


    一个!!.的例子:
    这里写图片描述


    参考: Kotlin中问号(?)和两个叹号(!!)


    展开全文
  • Kotlin 双冒号 :: 使用

    2017-06-05 17:08:25
    Kotlin 中 双冒号操作符 表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。先来看一下例子: fun main(args: Array) { println(lock("param1", "param2", ::getResult)) }/**...

    Kotlin 中 双冒号操作符 表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。先来看一下例子:

    
    fun main(args: Array<String>) {
        println(lock("param1", "param2", ::getResult))
    }
    
    /**
     * @param str1 参数1
     * @param str2 参数2
     */
    fun getResult(str1: String, str2: String): String = "result is {$str1 , $str2}"
    
    /**
     * @param p1 参数1
     * @param p2 参数2
     * @param method 方法名称
     */
    fun lock(p1: String, p2: String, method: (str1: String, str2: String) -> String): String {
        return method(p1, p2)
    }
    

    这里需要注意的是,lock 函数 的第三个参数传入 method 时,要确定参数个数、类型、返回值都和其形参一致。

    输出结果:

    result is {param1 , param2}

    如果我们需要调用其他 Class 中的某一个方法是:
    写法为:

    fun main(args: Array<String>) {
        var d = Test()
        println(lock("param1", "param2", d::getResult))
    }

    我们在 Class 中的某个方法中使用双冒号调用当前 Class 的内部方法时调动方式为:

    class Test1 {
        fun isOdd(x: Int) = x % 2 != 0
    
        fun test() {
            var list = listOf(1, 2, 3, 4, 5)
            println(list.filter(this::isOdd))
        }
    }

    一般情况,我们调用当前类的方法 this 都是可省略的,这里之所以不可省略的原因是

    为了防止作用域混淆 , :: 调用的函数如果是类的成员函数或者是扩展函数,必须使用限定符,比如this

    如果把 isOdd 写到 class 外部 (全局) 这里也是可以省略限定符。

    展开全文
  • 本文将展示在Android中会遇到的实际问题,并且使用Kotlin怎么去解决它们。一些Android开发者在处理异步、数据库或者处理Activity中非常冗长的listener时发现了很多的问题。通过一个个真实的场景,我们一边解决问题...
  • 点击上方“CSDN”,选择“置顶公众号”关键时刻,第一时间送达!毫无疑问,Kotlin 目前很受欢迎,业界甚至有人认为其将取代 Java 的霸主地位。它提供了 Null ...
  • kotlin是啥?这里就不用多说了,想必看这篇文章的童鞋肯定是有所了解的。 那么这篇文章你可以收获什么? 答:本文主要通过本人如何从java转战到kotlin并应用在实际项目中的个人经历,给大家提供一些学习思路、...
  • 简介:【静态、效率、表现力、安全、互动】 ● 新型静态类型编程语言 ● 提高工作效率和开发者满意度 ●现代并富有表现力(专注表达自己的想法、便于测试和维护...● Kotlin 可完全与 Java 编程语言互操作 ...
  • Kotlin世界中,无任何修饰的class、方法,等等默认就是public的,所以是隐式的 public的class class MyActivity { } public 的方法 fun methord() { } public 的变量 var temp: String? = null public的...
  • 为什么我要改用Kotlin

    2017-05-18 08:40:19
    写在前面的话,作为一个不熬夜的人,一觉醒来发现Kotlin成为了Android的官方语言,可谓是大喜过望。为了趁热打铁,我决定提前三天放出原定本周日Release的文章。希望能及时让大家了解一下Kotlin。相信很多开发人员,...
  • 作为一个假的Android开发者,并没有通宵去看 Google I/O 2017开发者大会。早上和往常 一样起床坐地铁上班,习惯性掏出手机看看...别人的项目重构也直接上的Kotlin,习惯了Kotlin写代码的简洁,高效,酷炫,写回Java
  • 标签: Kotlin 常用技巧 目录: 一、回调函数的Kotin的lambda的简化 二、内联扩展函数之let 三、内联函数之with 四、内联扩展函数之run 五、内联扩展函数之apply 六、内联扩展函数之also 七、let,with,run,...
  • Kotlin搞起来 —— 1.Kotlin学习资料与环境配置 学习资料 环境配置 附1:Java转Kotlin 附2:Kotlin转Java 附3:写Kotlin代码的小贴士
  • (新) Kotlin搞起来 —— 4.类与对象 1.Any类 2.类定义与对象实例化 3.构造器(构造方法) 主构造器 辅助构造器 私有主构造器 4.继承 5.接口 6.抽象类 7.内部类,匿名内部类的创建 8.单例对象 9.伴生对象 10.数据类 11....
  • Kotlin简介Kotlin早已是Android开发中的热门话题了,github上越来越多的项目是用Kotlin开发。但是这门语言一直处于很尴尬的境地,一方面自己不遗余力的挖掘自己在Android开发方面的作用,一方面却不是官方支持的语言...
  • 在移动应用开发方面,Kotlin和Flutter是目前比较火热的两门技术。其中,Kotlin是由Jetbrains研发,后被Google大力扶持,Flutter则是由Google自己独立研发的。从目前Google官方的态度来看,这两门技术都被无限看好,...
1 2 3 4 5 ... 20
收藏数 57,861
精华内容 23,144
关键字:

kotlin