• 定义类和构造方法:
class SimpleClass(var x: Int, val y: String){}//创建类不需要new关键字
val simpleClass = SimpleClass(9, "Hello")

构造方法放在类名的后面,如果x和y前面加了varval, x和y会分别在类中定义一个属性以及对应的getter和setter方法,不需要额外写,否则必须手动写getter和setter实现。类前面不需要修饰符public private等,跟java不一样,默认是public的。

像下面这样:

class Person(age: Int, name: String) {var age: Int = age //propertyget() {return field}set(value) {println("setAge: $value")field = value}var name: String = nameget() {return field // backing field}set(value) {field = value}
}

kotlin中,get和set方法可以显示写出来,但是一般默认不需要显示写出来,所以可以直接这样简写:

class Person(age: Int, name: String) {var age: Int = agevar name: String = name
}

这样也会默认生成get和set方法,除非你需要自定义get和set方法中的逻辑,那时就需要显示写出来了。

  • 定义成员变量必须提供初始值:
    var a : Int = 0var b : String = ""val c : Long = 0L

这与java也不同,java中定义变量不指定值有默认值,如int是0

  • 如果定义了初始值,变量默认具备get和set方法,可以直接引用变量读取值和赋值
 val simpleClass = SimpleClass(9, "Hello")println(simpleClass.a)simpleClass.a = 122println(simpleClass.a)
  • 定义变量时如果不提供默认值,则必须定义get和set方法, 快捷键可以提示
    var sss: Intget() {return 100}set(value) {}//常量属性val z : Longget() {return simpleProperty * 2L}
  • 接口定义:
interface SimpleInf {//与java不同,接口里面可以定义待实现的常量属性,由子类实现val simpleProperty: Int // propertyfun simpleMethod()
}

与java不同的是,接口里面由子类实现的常量属性,java的接口类中变量值只能是初始化固定的。

  • 实现接口:
class SimpleClass(var x: Int, val y: String) : SimpleInf {//覆写接口中的属性,如果是val则只有get方法 没有set方法,因为val是只读的override val simpleProperty: Intget() {return 2}//覆写方法必须加override关键字override fun simpleMethod() {}
}

与C++有点类似,实现接口直接在构造函数后面加冒号后写接口类名,覆写接口中的属性和方法都必须加override关键字。java也是需要的,但是java中不加顶多是警告,还是能运行的,但是kotlin不加直接编译报错。

  • 抽象类定义:
abstract class AbsClass {abstract fun absMethod()//抽象类中的非抽象方法必须添加open才能被子类复写,否则子类不能覆写open fun overridable(){}fun nonOverridable(){}
}

与java相同的是,kotlin抽象类中也可以有已实现的方法和纯抽象方法,但是,与java不同的是,如果子类要覆写抽象类里的已实现方法,必须在该方法前面加open关键字。也就是说未加open关键字的方法不能被覆写。

  • 实现抽象类:
open class SimpleClass(var x: Int, val y: String) : AbsClass() {//覆写方法必须加override关键字override fun absMethod() {}//如果想子类不能覆写某个override方法,添加final属性final override fun overridable(){}}

与接口一样,继承抽象类也是加冒号,不过后面的抽象类要写构造函数。同样,覆写抽象类中的方法全部都要加override关键字。另外,如果想要某个被覆写的方法不能再被子类覆写,加上final即可,与java一样。

  • 普通类继承:
open class SimpleClass(var x: Int, val y: String) {open fun zzz(string: String){}//final方法不能被覆写final override fun overridable(){}
}//继承类
class SimpleClass2(x: Int, y: String): SimpleClass(x, y){override fun zzz(string: String){}}

如果类要被子类继承,则要被继承的类名和方法名前面都要加open关键字,否则不能被继承和覆写。

  • 属性引用:
class Person(age: Int, name: String) {var age: Int = agevar name: String = name
}fun main() {val ageRef = Person::ageval person = Person(18, "Bennyhuo")val nameRef = person::name //绑定接受者的属性引用,调用set的时候可以不用传接受者ageRef.set(person, 20)nameRef.set("Andyhuo")
}

扩展方法和扩展属性:

class PoorGuy{var pocket: Double = 0.0
}//定义类的扩展方法
fun PoorGuy.noMoney() {println("noMoney")
}//定义类的扩展属性 property = backing field + getter + setter
var PoorGuy.moneyLeft: Doubleget() {return this.pocket}set(value) {pocket = value}

就是在类定义大括号之外,再后续给类定义方法和属性,有点像java静态方法的调用形式,但这样定义的是成员方法和属性,并不是java中那样的静态方法。

fun main() {val poorGuy = PoorGuy()poorGuy.noMoney()println(poorGuy.moneyLeft)poorGuy.moneyLeft = 10000.0println(poorGuy.moneyLeft)
}

类和扩展方法不一定在同一个kt文件中,但必须在方法的外层定义,即不能在某个函数方法中定义,必须是顶层的,如不能在main方法中定义。

fun Person.eat(s : String) {println("eat$s")
}var Person.howOld: Intget() {return this.age}set(value) {age = value}fun main() {val eat = Person::eatval person = Person(10, "张三")person.eat("aaa")eat(person, "222")println(person.howOld)
}

上面文件中只要导入Person这个类就可以给它定义扩展方法。

  • 给系统类添加扩展方法:
//给String类定义扩展方法 给String前后加count个空格
fun String.padding(count: Int, char: Char = ' '): String {//生成重复count次的空格连续串val padding = (1 .. count).joinToString(""){ char.toString() }return "${padding}${this}${padding}"
}//给String类定义扩展方法 判断字符串是否是邮箱
fun String.isEmail(): Boolean {return matches(Regex("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"))
}//给String类定义扩展方法 字符串重复count次
fun String.times(count: Int): String {return (1 .. count).joinToString("") { this }
}

上面是给String类定义扩展方法,使用起来简单:

fun main() {println("admin@bennyhuo.com".isEmail())println("Hello".padding(10))println("*".times(10))//定义对扩展方法的引用val stringTimes = String::timesval stringTimesBound = "*"::times}

只要字符串点方法名即可, 看到这个顿时感觉便捷性这块kotlin确实比java强了太多!

空类型安全:

  • kotlin中,指定类型的变量不能赋值为null,即空类型安全
  var nonNull: String = "Hello"nonNull = null //赋值为空,这一行编译器会报错val length = nonNull.length //这样使用是安全的

就是说kotlin中明确的类型是不能赋值为一个null值的,这与java明显不同。

  • 定义可接受null类型的变量,在类型后面加一个
   var nullable: String? = "Hello"val length = nullable?.length //安全访问可能为空的变量

这时变量可能为null, 所以要判空,判空方式 ?. 比java简洁,有点类似js语法。

但是用 ?.操作符之后,如果变量为null,则最终等号左边的变量结果 也可能为null, 因此在使用length之前还要再判断length是否为null.

   var nullable: String? = "Hello"val length = nullable?.length ?: 0 //确保length不为空的写法 等价三目运算 boolean? a : b

其中操作符 ?:等价于三目运算符,length 为null 返回右边的,不为null返回左边的。

说明: String 类型是 String? 类型的子类, Int 类型是 Number 类型的子类,使用遵循里氏替换原则:所有使用父类的地方都可以使用子类替换,反之则不行

    var x: String = "Hello"var y: String? = "World"//    x = y // Type mismatchy = x // OKvar a: Int = 2var b: Number = 10.0//    a = b // Type mismatchb = a // OK
  • 引用其他平台语言的对象要使用?. 判空:

java类

public class Person {@Nullablepublic String getTitle(){return null;}
}

kotlin类

  val person = Person() // 创建一个java的Person类实例val title = person.title // 此时的title类型是java平台的String类型,不是kotlin的String类型//kotlin中无法判断title的实际类型是来自哪个平台的,所以主动添加?.可确保安全访问val titleLength = title?.length

由于kotlin 支持java、javascript、native三个平台,并且在android项目中支持java和kotlin类混用,因此当在kotlin中导入java类的时候,kotlin无法判断导入类创建的对象属性的实际类型是来自哪个平台的,所以主动添加?.可确保安全访问。

下面的例子也是一样:

    val file = File("abc") //这个File类是java的val files = file.listFiles()//因此这里的files使用可能为空,应该使用files?.sizeprintln(files.size)

所以要注意导入的类是不是java的。当然有时出现可能为空时编译器也会给出提示,可以点击快速修复即可。但是java方法上没有加@Nullable注解的话,编译器也无法识别,这时要人工判断了。

  • 类型强转:
    val kotliner: Kotliner = Person("benny", 20)if(kotliner is Person){println((kotliner as Person).name)}

使用 is 判断是否是实例,对比java的instanceof
使用 as 转换类型,对比java的小括号强制转换
那如果类型转换失败呢,在java中不try-catch会直接抛异常,在kotlin中提供的形式安全访问:

    val kotliner: Kotliner = Person("benny", 20)if(kotliner is Person){println((kotliner as? Person)?.name)}

(kotliner as? Person)?.name 其中 as? 表示转换失败返回null, 所以name前面也同时使用?.保证安全访问。

  • 智能类型转换:
    var value: String? = nullvalue = "benny" //如果显示的经过了类型赋值,在下面的使用是安全的
//    if(value != null){println(value.length);//这里kotlin会隐式的转成kotlin的String类型,不需要大括号//value = null
//    }

我的理解是显示的经过了类型赋值以后,那么再去.引用属性的时候就是安全的,因为kotlin转成了它的内部确定类型,不可能为null。

慕课网上课程介绍说这里的value在大括号里面会转成Sting类型,出了大括号,value又会变成String?类型。我认为他这里说法是错误的,只是在调用.length时才会自动转换,如果调用完了会即刻恢复,而不是出了大括号。你可以把上面注释的代码恢复,你会发现在大括号中是可以给它赋值null的,这说明它还是String? 而不是String, 因为String是不能接受null的。

  • 外部变量不支持智能类型转换:
var tag: String? = nullfun main() {if(tag != null){//对顶级变量的使用会有风险,即便判断了null, 因为可能有其他线程修改了它的类型println(tag.length) }
}

这里if即便判断了null ,还是会有风险,可能有线程安全问题,还是推荐使用 ?.安全访问。

总体来说,kotlin也会有null需要判空的情况,只不过判空的写法比java更简洁。但是在大多数情况下,你只要指定了变量定义的具体类型如String、Int就不会出现java的空指针问题。

Kotin学习手记——类、接口、扩展方法、空类型相关推荐

  1. Kotin学习手记——类,android手机开发

    return field } set(value) { println("setAge: $value") field = value } var name: String = n ...

  2. ASP.Net string 类的扩展方法 [转]

    string 类的扩展方法列表(基本相同于 IEnumerable<T> 接口的成员列表): Aggregate<> //累加 All<> //是否都满足条件 An ...

  3. string 类的扩展方法

    string 类的扩展方法 分类: 2011年技术文章2011-05-11 18:14 76人阅读 评论(0) 收藏 举报 string 类的扩展方法列表(基本相同于 IEnumerable<T ...

  4. Unity学习笔记--Extension Method 扩展方法

    引言 最近跟着麦克学习Unity的时候,学习到了类的扩展方法.比如你现在有一个已有的类,你想给它加一个方法,当然如果是你自己写的类可以直接在这个方法里面写,但是如果是Unity自带的类,我们不可能直接 ...

  5. 设计一个形状类(接口)Shape,方法:求周长和求面积形状类(接口)的子类(实现类):

    设计一个形状类(接口)Shape,方法:求周长和求面积 形状类(接口)的子类(实现类): package zuoye2;public interface Shape {// 求面积方法double g ...

  6. 设计一个形状类(接口)Shape,方法:求周长和求面积

    3.设计一个形状类(接口)Shape,方法:求周长和求面积形状类(接口)的子类(实现类):Rect(矩形)Circle(圆形)Rect类的子类:Square(正方形)不同的子类会有不同的计算周长和面积 ...

  7. Java基础 接口实现 设计一个形状类(接口)Shape,方法:求周长和求面积

    题目: 设计一个形状类(接口)Shape,方法:求周长和求面积形状类(接口)的子类(实现类);:Rect(矩形),Circle(圆形)Rect类的子类:Square(正方形)不同的子类会有不同的计算周 ...

  8. 设计一个形状类(接口)Shape,方法:求周长和求面积形状类(接口)的子类(实现类): Rect(矩形) Circle(圆形)Rect类的子类: Square(正方形)不同的子类会有不同的计算周长和面

    设计一个形状类(接口)Shape,方法:求周长和求面积 形状类(接口)的子类(实现类):     Rect(矩形)     Circle(圆形) Rect类的子类:     Square(正方形)   ...

  9. 【java笔记】类/接口作为成员变量类型

    类/接口  作为成员变量 以英雄角色为例,有姓名,年龄,武器(类),技能(接口),攻击(方法) 技能接口: public interface Skill {void use();//使用技能的抽象方法 ...

最新文章

  1. 如何利用Python调用一些搜索引擎网站?
  2. win7任务栏还原为xp样式
  3. c语言 文件 long double 读取,读取*.wav音频文件
  4. SQL Server 2008 阻止保存要求重新创建表的更改问题
  5. c语言面试题东软,2012东软笔试题
  6. python车牌识别系统开源代码_python实现车牌识别的示例代码
  7. 传统算法与神经网络算法,神经网络是谁提出的
  8. gcc 源码下载地址
  9. 目标检测中的边界框(x,y,w,h形式转换与绘制)
  10. 深入解析内存原理:DRAM的基本原理
  11. fabric2.0 概念, peer、账本和排序服务
  12. 编程大佬行为准则:一杯茶一包烟,一行代码写一天
  13. Thingsboard 物联网平台 CoAP 协议介绍
  14. [kubernetes]-weave scope初体验
  15. Ajax 异步同步请求
  16. 在同一个浏览器上打开同一个网址只打开一个窗口的方法
  17. 敏感性、特异度、α、β、阳性预测值(PPV)、阴性预测值(NPV)等指标及置信区间计算(附R语言代码)
  18. LOGO设计的五大基础原则
  19. uml通信图画法_[UML]UML系列——协作图(通信图)collaboration diagram
  20. 三种将list转换为map的方法

热门文章

  1. Authing 郑凌:我眼中的 Authing
  2. 如何写自己的lib文件并测试
  3. 【Get深一度】相位差波束指向角推导-相控阵雷达原理
  4. 组织行为学对项目管理的意义(1)
  5. Lua(一)——Lua介绍
  6. excel控件只为简单写入数据表--github找到ExcelUtil笔记
  7. 「硬见小百科」几款常用的保护电路
  8. Hexo博客Next主题bilibili视频Markdown插入文章
  9. 现代检测技术--概论
  10. excel中提取月份_在Excel中自动添加月份表