类和对象
/* -------------- 类和对象 -------------- */
/**
Kotlin 类可以包含:构造函数和初始化代码块、函数、属性、内部类、对象声明。
class Runoob { // 类名为 Runoob
// 大括号内是类体构成
}
声明空类
class Empty
成员函数
class Runoob {
fun foo(){}
}
类的属性
class Runoob {
var name:String
var url:String
var city:String
}
实例的创建
val site = Runoob()
属性的引用
site.name="zzz"
site.url="action.call"
构造方法
class Person constructor(firstName:String){}
如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略。
class Person(firstName:String){}
getter 和 setter
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
getter 和 setter 都是可选
如果属性类型可以从初始化语句或者类的成员函数中推断出来,那就可以省去类型,val不允许设置setter函数,因为它是只读的。
var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter
val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化
val inferredType = 1 // 类型为 Int 类型,默认实现 getter
*/
class Person {
var lastName: String = "zhang"
get() = field.toUpperCase()
set
/*Kotlin 中类不能有字段。提供了 Backing Fields(后端变量) 机制,备用字段使用field关键字声明,field 关键词只能用于属性的访问器*/
var no: Int = 100
get() = field
set(value) {
if (value < 10) {
field = value
} else {
field = -1
}
}
var height: Float = 145.4f
private set
}
fun t_obj_person() {
val person = Person()
person.lastName = "ratel"
println("lastName = ${person.lastName}")
person.no = 9
println("no = ${person.no}")
person.no = 21
println("no = ${person.no}")
}
public class MyTest {
// 非空属性必须在定义的时候初始化,kotlin提供了一种可以延迟初始化的方案,使用 lateinit 关键字描述属性
lateinit var subject: TestSubject
@SetUp
fun setup() {
subject = TestSubject()
}
@Test
fun test() {
subject.method()
}
}
annotation class SetUp
annotation class Test
class TestSubject {
fun method() {
}
}
/**主构造器
* 主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码使用init关键字
*/
class BookBean constructor(name: String) {
init {
println("book name $name")
}
}
/**
* 注意:主构造器的参数可以在初始化代码段中使用,也可以在类主体n定义的属性初始化代码中使用。 一种简洁语法,可以通过主构造器来定义属性并初始化属性值(可以是var或val):
* val 定义的常量是不可以修改的
* 如果构造器有注解,或者有可见度修饰符,这时constructor关键字是必须的,注解和修饰符要放在它之前。
*/
class People(val firstName: String, val lastName: String) {
init {
println("firstName $firstName , lastName $lastName")
}
}
fun t_consutrctor() {
BookBean("三国")
People("kagle", "ai")
}
class Runoob constructor(name: String) {
var url = "http://www.runoob.com"
var contry: String = "CN"
var siteName = name
init {
println("初始化网站名: $name")
}
fun printTest() {
println("我是类的函数")
}
}
fun t_runoob() {
val runoob = Runoob("菜鸟教程")
println(runoob.siteName)
println(runoob.url)
println(runoob.contry)
runoob.printTest()
}
/**
* 次构造函数
类也可以有二级构造函数,需要加前缀 constructor:
*/
class NewPerson {
private val children: Children = Children()
constructor(parent: NewPerson) {
parent.children.add(this)
}
}
/**
* 如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。
* 在同一个类中代理另一个构造函数使用 this 关键字:
*/
class NewPerson2 constructor(val name: String) {
var age: Int = 0
init {
println("NewPerson2 init name : $name")
}
constructor(name: String, _age: Int) : this(name) {
age = _age
// 初始化
println("NewPerson2 次构造 : name : $name , age : $age")
}
}
fun t_new_person2() {
val newPerson2 = NewPerson2("zzg", 32)
println("user name ${newPerson2.name} , age ${newPerson2.age}")
}
class Children {
var pList = ArrayList<NewPerson>()
fun add(person: NewPerson) {
if (!pList.contains(person)) {
pList.add(person)
}
}
}
/**
*如果一个非抽象类没有声明构造函数(主构造函数或次构造函数),它会产生一个没有参数的构造函数。构造函数是 public 。如果你不想你的类有
* 公共的构造函数,你就得声明一个空的主构造函数
*/
class DontCreateMe private constructor() {
}
/**
* 注意:在 JVM 虚拟机中,如果主构造函数的所有参数都有默认值,编译器会生成一个附加的无参的构造函数,这个构造函数会直接使用默认值。这使得 Kotlin 可以更简单的使用像 Jackson 或者 JPA 这样使用无参构造函数来创建类实例的库。
class Customer(val customerName: String = "")
*/
/* -------------- 类和对象 -------------- */
/* -------------- 抽象类 -------------- */
/**
* 抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的。抽象成员在类中不存在具体的实现。
注意:无需对抽象类或抽象成员标注open注解。
*/
open class BaseHandler {
open fun f() {}
}
// 抽象类
abstract class BaseDefaultHanlder : BaseHandler() {
// 抽象方法
override abstract fun f()
}
/* -------------- 抽象类 -------------- */
/* -------------- 嵌套类(静态内部类) -------------- */
class Outer {
private val bar: Int = 1
class Nested {
var ot: Outer = Outer()
fun test() {
// 嵌套类可以引用外部类私有变量,但要先创建外部类的实例,不能直接引用
println(ot.bar)
}
fun foo() = 2
}
}
// 调用格式:外部类.嵌套类.嵌套类方法/属性
fun t_nest_cls() {
// 嵌套类,Outter后边没有括号
val foo = Outer.Nested().foo()
println("foo = $foo")
}
/* -------------- 嵌套类(静态内部类) -------------- */
/* -------------- 内部类 -------------- */
/**
* 内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
*/
class Outer2 {
private val bar: Int = 2
var v = "成员属性"
/*嵌套内部类*/
inner class Inner2 {
// 访问外部类成员
fun foo() = bar
fun innterTest() {
// 获取外部类的成员变量
// 相当于Outer.this
/*
为了消除歧义,要访问来自外部作用域的 this,我们使用this@label,
其中 @label 是一个 代指 this 来源的标签。
*/
var o = this@Outer2
println("Outer2 内部类可以引用外部类的成员: v " + o.v)
}
}
}
fun t_inner_cls() {
// 内部类,Outter后边有括号
//
val foo = Outer2().Inner2().foo()
println("t_inner_cls foo = $foo")
val d = Outer2().Inner2().innterTest()
println("t_inner_cls d = $d")
}
/* -------------- 内部类 -------------- */
/* -------------- 匿名内部类 -------------- */
/*使用对象表达式来创建匿名内部类*/
class Test2 {
var v = "成员属性"
fun setInterFace(test: ITest) {
println("Test2 setInterFace v = $v ")
test.test()
}
}
// 定义接口
interface ITest {
fun test()
}
fun t_anonymous() {
val test2 = Test2()
/**
* 采用对象表达式来创建接口对象,即匿名内部类的实例
*/
test2.setInterFace(object : ITest {
override fun test() {
println("接口回调,对象表达式创建匿名内部类的实例")
}
})
}
/* -------------- 匿名内部类 -------------- */
/* -------------- 类的修饰符 -------------- */
/**
* 类的修饰符包括 classModifier 和_accessModifier_:
* classModifier: 类属性修饰符,标示类本身特性。
abstract // 抽象类
final // 类不可继承,默认属性
enum // 枚举类
open // 类可继承,类默认是final的
annotation // 注解类
accessModifier: 访问权限修饰符
private // 仅在同一个文件中可见
protected // 同一个文件中或子类可见
public // 所有调用的地方都可见
internal // 同一个模块中可见
*/
private fun foo() {} // 在包类可见,包级函数
public var bar: Int = 5 // 该属性随处可见
internal val baz = 6 // 相同模块内可见
/* -------------- 类的修饰符 -------------- */
fun main(args: Array<String>) {
t_obj_person()
t_consutrctor()
t_runoob()
t_new_person2()
t_nest_cls()
t_inner_cls()
t_anonymous()
}
继承
/* -------------- 继承 -------------- */
/**
kotlin中所有的类都继承Any,它是所有类的超父,对于没有超类型声明的类默认继承超类
class Exemaple // 从Any隐式继承
Any默认提供三个函数:equals,hashCode,toString
Any不是java.lang.Object
如果一个类要被继承,可以使用open关键字进行修饰
*/
open class Base(p: Int) // 定义基类
class Dervied(p: Int) : Base(p)
/* -------------- 继承 -------------- */
/* -------------- 构造函数 -------------- */
/**
*子类有主构造函数
如果子类有主构造函数, 则基类必须在主构造函数中立即初始化
*/
open class New_EPerson(var name: String, var age: Int) { // 基类
}
class Student(name: String, age: Int, var no: String, var score: Int) : New_EPerson(name, age) {
}
fun t_ext_obj() {
val student = Student("runnob", 18, "S0035", 89)
println("姓名: ${student.name}")
println("年龄: ${student.age}")
println("学生号: ${student.no}")
println("学分: ${student.score}")
}
/**
*子类没有主构造函数
如果子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类,
或者在代理另一个构造函数。初始化基类时,可以调用基类的不同构造方法。
*/
open class Animal constructor(ctx: Context) {
// 次构造
constructor(ctx: Context, attrs: AttributeSet) : this(ctx)
}
class Cat : Animal {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
//实例
open class User(name: String) {
// 次级构造函数
constructor(name: String, age: Int) : this(name) {
println("---------- 基类次级构造函数 ----------")
}
}
class Worker : User {
constructor(name: String, age: Int, no: String, score: Int) : super(name, age) {
println("---------- 继承类次级构造函数 ----------")
println("Worker 姓名: $name")
println("Worker 年龄: $age")
println("Worker 学生号: $no")
println("Worker 学分: $score")
}
}
fun t_ext_user() {
Worker("test01", 23, "S1174", 135)
}
/**
*重写
在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,
那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词
*/
// 用户基类
open class NewUser {
open fun study() { // 允许子类重写
println("我毕业了")
}
}
/*子类继承NewUser类*/
class NewStudent : NewUser() {
override fun study() { // 重写方法
println("我在读大学")
}
}
fun t_ode() {
val student = NewStudent()
student.study()
}
// 如果有多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现。
open class A {
open fun f() {
println("A")
}
fun a() {
println("a")
}
}
interface B {
// //接口的成员变量默认是 open 的
fun f() {
println("B")
}
fun b() {
println("b")
}
}
class C() : A(), B {
override fun f() {
println("C run f ... ")
// 选择性的去调用父类的实现
super<A>.f()
super<B>.f()
}
}
fun t_oc() {
val c = C()
c.f()
}
/**
* 属性重写
属性重写使用 override 关键字,属性必须具有兼容类型,每一个声明的属性都可以通过初始化程序或者getter方法被重写:
*/
open class Foo {
open val x: Int
get() = 10
}
class Bar1 : Foo() {
override val x: Int
get() = super.x
}
/**
*
你可以用一个var属性重写一个val属性,但是反过来不行。因为val属性本身定义了getter方法,重写为var属性会在衍生类中额外声明一个setter方法
你可以在主构造函数中使用 override 关键字作为属性声明的一部分:
*/
interface IFoo {
val count: Int
}
class Bar_1(override val count: Int) : IFoo
class Bar_2 : IFoo {
override var count: Int = 0
}
/* -------------- 构造函数 -------------- */
fun main(args: Array<String>) {
t_ext_obj()
t_ext_user()
t_ode()
t_oc()
}
接口
/* -------------- 接口 -------------- */
//接口与java8类似,使用interface定义,允许方法有默认实现
interface IMe {
fun bar()
fun foo() {
// 默认实现
println("foo")
}
}
/**
* 实现接口
* 一个类或者对象可以实现一个或多个接口
*/
class MyChild : IMe {
override fun bar() {
println("bar")
}
}
fun t_mychild() {
val myChild = MyChild()
myChild.bar()
myChild.foo()
}
/**
* 接口中的属性
* 接口中的属性只能是抽象的,不允许初始化值,接口不会保存属性值,实现接口时,必须重写属性
*/
interface INewMe {
// 抽象属性
var name: String
}
class NewMeImpl : INewMe {
// 重写属性
override var name: String = "runoob"
}
fun t_new_me() {
val meImpl = NewMeImpl()
println("name = ${meImpl.name}")
}
/**
* 函数的重写
* 实现多个接口时,可能会遇到同一方法继承多个实现的问题
*/
interface A_1 {
fun foo() {
println("A_1")
}
fun bar()
}
interface B_1 {
fun foo() {
println("B_1")
}
fun bar() {
println("B_1 bar")
}
}
class C_1 : A_1 {
override fun bar() {
println("C_1 bar")
}
}
class D_1 : A_1, B_1 {
override fun bar() {
println("D_1 bar")
super<B_1>.bar()
}
override fun foo() {
println("D_1 foo")
super<A_1>.foo()
super<B_1>.foo()
}
}
fun t_o_method() {
val d_1 = D_1()
d_1.foo()
d_1.bar()
}
/**
* 实例中接口 A 和 B 都定义了方法 foo() 和 bar(), 两者都实现了 foo(), B 实现了 bar()。因为 C 是一个实现了 A 的具体类,
* 所以必须要重写 bar() 并实现这个抽象方法。
然而,如果我们从 A 和 B 派生 D,我们需要实现多个接口继承的所有方法,并指明 D 应该如何实现它们。
这一规则 既适用于继承单个实现(bar())的方法也适用于继承多个实现(foo())的方法。
*/
/* -------------- 接口 -------------- */
fun main(args: Array<String>) {
t_mychild()
t_new_me()
t_o_method()
}
扩展
package com.example.ext
/* -------------- 扩展 -------------- */
/**
* kotlin可以对一个类的属性和方法进行扩展,且不需要使继承或使用Decorator模式
* 扩展是一种静态行为,对于被扩展的类代码本身不会造成任何影响
*/
/**
* 扩展函数
* 扩展函数可以在已有的类中添加新的方法,不会对原类做修改
* fun receiverType.functionName(params){
* body
* }
*
* receiverType 要扩展的类
* functionName 方法名称
*/
class New_User(var name: String)
// 扩展函数
fun New_User.Print() {
println("User .. $name")
}
fun t_user_print() {
val user = New_User("zz")
user.Print()
}
/**
* 为MutableList添加一个swap函数,交换不同位置的值
*/
fun MutableList<Int>.swap(_i: Int, _j: Int) {
var tmp = this[_i]
this[_i] = this[_j]
this[_j] = tmp
// this关键字指代接收者对象(receiver object)(也就是调用扩展函数时, 在点号之前指定的对象实例)。
}
fun t_change_item() {
val l = mutableListOf(1, 2, 3)
l.swap(0, 2)
println(l.toString())
}
/**
* 扩展函数是静态解析的
扩展函数是静态解析的,并不是接收者类型的虚拟成员,在调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式来决定的,
而不是动态的类型决定的:
*/
open class EC
class ED : EC()
fun EC.foo() = "c" // 扩展函数foo
fun ED.foo() = "d" // 扩展函数foo
/**
* 无论方法传递的是哪个实例,最终只会调用EC的扩展函数
*/
fun printFoo(c: EC) {
// 类型是c类型
// c
println(c.foo())
}
fun t_ext_method_2() {
// 这里会走EC的扩展函数
printFoo(ED())
// 这里才会走ED的扩展函数
println(ED().foo())
}
/**
* 若扩展函数和成员函数一致,则使用该函数时,会优先使用成员函数。
*/
class NC {
fun foo() {
println("成员函数")
}
}
fun NC.foo() {
println("扩展函数")
}
fun t_ext_method_3() {
val nc = NC()
nc.foo()
}
/**
* 扩展一个空对象
在扩展函数内, 可以通过 this 来判断接收者是否为 NULL,这样,即使接收者为 NULL,也可以调用扩展函数
*/
fun Any?.toString(): String {
if (this == null)
return "null"
// 空检测之后,this自动转换为非空类型,所以下面的toString
// 解析为Any类的成员函数
return toString()
}
fun t_ext_nul_obj() {
var t = null
println(t.toString())
}
/**
* 扩展属性
除了函数,Kotlin 也支持属性对属性进行扩展:
扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。初始化属性因为属性没有后端字段(backing field),所以不允许被初始化,只能由显式提供的 getter/setter 定义。
val Foo.bar = 1 // 错误:扩展属性不能有初始化器
扩展属性只能被声明为 val。
*/
val <T> List<T>.lastIndex: Int
get() = size - 1
/**
*
伴生对象的扩展
如果一个类定义有一个伴生对象 ,你也可以为伴生对象定义扩展函数和属性。
伴生对象通过"类名."形式调用伴生对象,伴生对象声明的扩展函数,通过用类名限定符来调用
*/
class MyCls {
companion object {
// 将称为Companion
}
}
// 扩展函数
fun MyCls.Companion.foo() {
println("伴随对象的扩展函数")
}
// 扩展字段
val MyCls.Companion.no: Int
get() = 10
fun t_ext_companion() {
println("no: ${MyCls.no}")
MyCls.foo()
}
/**
* 扩展的作用域
* 通常扩展函数或属性定义在顶级包下:
package foo.bar
fun Baz.goo() { …… }
要使用所定义包之外的一个扩展, 通过import导入扩展的函数名进行使用:
package com.example.usage
import foo.bar.goo // 导入所有名为 goo 的扩展
// 或者
import foo.bar.* // 从 foo.bar 导入一切
fun usage(baz: Baz) {
baz.goo()
}
*/
/**
* 扩展声明为成员
在一个类内部你可以为另一个类声明扩展。
在这个扩展中,有个多个隐含的接受者,其中扩展方法定义所在类的实例称为分发接受者,而扩展方法的目标类型的实例称为扩展接受者。
*/
// 扩展接受者
class EN_D {
fun bar() {
println("EN_D bar")
}
}
// 分发接受者
class EN_C {
fun baz() {
println("EN_C baz")
}
fun EN_D.foo() {
println("EN_C ext foo")
bar()
baz()
}
fun caller(d: EN_D) {
println("EN_C caller")
// 调用扩展函数
d.foo()
}
}
fun t_ext_mem() {
println("t_ext_mem")
val c: EN_C = EN_C()
val d: EN_D = EN_D()
c.caller(d)
}
/**
* 假如在调用某一个函数,而该函数在分发接受者和扩展接受者均存在,则以扩展接收者优先,
* 要引用分发接收者的成员你可以使用限定的 this 语法。
*/
class END {
fun bar() {
println("END bar")
}
}
class ENC() {
fun bar() {
println("ENC bar")
}
fun END.foo() {
println("ENC foo")
// 调用END.bar(),扩展接收者优先
bar()
this@ENC.bar() // 调用ENC.bar
}
fun caller(d: END) {
println("ENC caller")
// 调用扩展函数
d.foo()
}
}
fun t_ext_mem_2() {
println("t_ext_mem_2")
val enc = ENC()
val end = END()
enc.caller(end)
}
// 调用 ->> 分发接收者 ->> 扩展接收者
/**
* 以成员的形式定义的扩展函数, 可以声明为 open , 而且可以在子类中覆盖. 也就是说, 在这类扩展函数的派 发过程中,
* 针对分发接受者是虚拟的(virtual), 但针对扩展接受者仍然是静态的。
*/
open class EN2D {
}
class N2D : EN2D() {
}
open class EN2C {
open fun EN2D.foo() {
println("EN2D.foo in EN2C")
}
open fun N2D.foo() {
println("N2D.foo in EN2C")
}
fun caller(d: EN2D) {
println("EN2C caller")
// 调用扩展函数
d.foo()
}
}
class N2C : EN2C() {
override fun EN2D.foo() {
println("EN2D.foo in N2C")
}
override fun N2D.foo() {
println("N2D.foo in N2C")
}
}
fun t_ext_mem_3() {
println(" t_ext_mem_3 ")
println(" -------------- ")
EN2C().caller(EN2D()) // EN2D.foo in EN2C
println(" -------------- ")
N2C().caller(EN2D()) // EN2D.foo in N2C
println(" -------------- ")
EN2C().caller(N2D()) // EN2D.foo in EN2C
}
class MyNewCls {
companion object {
val mFid1: Int = 1
var mFid2 = "this is myCFid2"
fun companionFun1() {
println("MyNewCls this is 1st companion func")
foo()
}
fun companionFun2() {
println("MyNewCls this is 2st companion func")
companionFun1()
}
}
fun MyNewCls.Companion.foo() {
println("MyNewCls 伴随对象的扩展函数 (内部) ")
}
fun test2() {
println("MyNewCls test2 ")
MyNewCls.foo()
}
init {
println("MyNewCls init ")
test2()
}
}
val MyNewCls.Companion.no: Int
get() = 10
fun MyNewCls.Companion.foo() {
println("MyNewCls.Companion foo 伴随对象外部扩展函数")
}
fun t_ext_mem_4() {
println("t_ext_mem_4")
println("no: ${MyNewCls.no}")
println("filed1:${MyNewCls.mFid1}")
println("filed2:${MyNewCls.mFid2}")
MyNewCls.foo()
MyNewCls.companionFun2()
}
/* -------------- 扩展 -------------- */
fun main(args: Array<String>) {
t_user_print()
t_change_item()
t_ext_method_2()
t_ext_method_3()
t_ext_nul_obj()
t_ext_companion()
t_ext_mem()
t_ext_mem_2()
t_ext_mem_3()
t_ext_mem_4()
}
数据类和密封类
/* -------------- 数据类 -------------- */
/**
kotlin可以创建一个只包含数据的类:data
data class User(val name:String,val age:Int)
编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
equals() / hashCode()
toString() 格式如 "User(name=John, age=42)"
componentN() functions 对应于属性,按声明顺序排列
copy() 函数
如果这些函数在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。
为了保证生成代码的一致性以及有意义,数据类需要满足以下条件:
主构造函数至少包含一个参数。
所有的主构造函数的参数必须标识为val 或者 var ;
数据类不可以声明为 abstract, open, sealed 或者 inner;
数据类不能继承其他类 (但是可以实现接口)。
*/
/**
* 复制
复制使用 copy() 函数,我们可以使用该函数复制对象并修改部分属性
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
*/
data class Account(val name: String, val age: Int)
fun t_data() {
var jack = Account(name = "Jack", age = 1)
// 复制并修改部分属性
val olderJack = jack.copy(age = 2)
println(jack)
println(olderJack)
}
/**
* 数据类以及解构声明
* 组件函数允许数据在解构声明中使用
*/
fun t_data_dec_con() {
val jane = Account("jane", 35)
val (name, age) = jane
println("$name , $age years of age") // jane , 35 years of age
}
/**
* 标准数据类
标准库提供了 Pair 和 Triple 。在大多数情形中,命名数据类是更好的设计选择,因为这样代码可读性更强而且提供了有意义的名字和属性。
*/
/* -------------- 数据类 -------------- */
/* -------------- 密封类 -------------- */
/**
* 密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:
* 枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。
声明一个密封类,使用 sealed 修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中。
sealed 不能修饰 interface ,abstract class(会报 warning,但是不会出现编译错误)
*/
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
/*
将类的声明和定义该类的单例对象结合在一起(即通过object就实现了单例模式)
即将class关键字替换为object关键字,来声明一个类,与此同时也声明它的一个对象。只要编写这么多代码,这个类就已经是单例的了
换句话说,object declaration的类最终被编译成:一个类拥有一个静态成员来持有对自己的引用,并且这个静态成员的名称为INSTANCE,当然这个INSTANCE是单例的,故这里可以这么去使用。如果用Java代码来声明这个RepositoryManager的话,可以有如下代码:
class RepositoryManager{
private RepositoryManager(){}
public static final RepositoryManager INSTANCE = new RepositoryManager();
}
*/
object NotANumber : Expr()
// 使用密封类的关键好处在于使用 when 表达式 的时候,如果能够 验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了。
fun eval(expr: Expr): Double = when (expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
class ObjectOuter {
object Inner {
fun method() {
println("I'm in inner class")
}
}
}
fun t_object_cls() {
ObjectOuter.Inner.method()
}
/* -------------- 密封类 -------------- */
fun main(args: Array<String>) {
t_data()
t_data_dec_con()
println("eval_1 ${eval(Const(35.0))}")
println("eval_2 ${eval(Sum(Const(35.0), Const(35.0)))}")
println("eval_2 ${eval(NotANumber)}")
t_object_cls()
}