kotlin中类和属性

1、kotlin中的类

1.1 类的声明

Kotlin 中使用关键字 class 声明类

class Invoice{.......}

类声明由类名、类头(指定其类型参数、主构造函数等)以及由花括号包围的类体构成。类头与类体都是可选的; 如果一个类没有类体,可以省略花括号

class Empty

在kotlin中只有数据没有代码的类通常被称为值对象

class Person(val name :String)//值对象

在kotlin中public 是默认的修饰符,可以在类中直接省略!!!

1.2 属性

在java中 字段和其访问器的组合通常被叫做属性,在kotlin中属性是头等语言特性,完全代替了字段和访问器方法,在类中声明一个属性和声明一个变量一样,使用val和var。声明称val的属性是只读的,var属性是可变的

val 是只读属性  作用:生成一个字段和一个简单的getter
var 是可写属性 作用:生成一个字段 一个getter方法和一个setter方法
注意获取其属性的方法名字 是get+字段名首字母大写。setter也是。其中如果字段名是is开头的会特殊。
lateinit :我们在定义属性的时候,通常会在非空属性上使用lateinit 修饰符表明这个属性会将初始化推迟到构造方法后调用

1.2.1自定义访问器

class Rect(val height : Int,val width :Int){
			val isSquare :Boolean
                     get(){ //声明属性是getter
                      return height ==width 
                        }			
}
调用的时候 val reactangele =Rect(41.41)
reactangele.isSquare

1.2.2 幕后字段

在Kotlin中, 如果属性至少一个访问器使用默认实现,那么Kotlin会自动提供幕后字段,用关键字field表示,幕后字段主要用于自定义getter和setter中,并且只能在getter 和setter中访问。

class Person {
    var name:String = ""
        get() = field 
        set(value) {
            field = value
        }
}
// 例子二
class Person {
    var name:String = ""
}

两个属性的声明是等价的,例子一中的getter和setter 就是默认的getter和setter。其中幕后字段field指的就是当前的这个属性,它不是一个关键字,只是在setter和getter的这个两个特殊作用域中有着特殊的含义,就像一个类中的this,代表当前这个类。

class Person(var gender:Gender){
    var name:String = ""
        set(value) {
            field = when(gender){
                Gender.MALE -> "Jake.$value"
                Gender.FEMALE -> "Rose.$value"
            }
        }
}

enum class Gender{
    MALE,
    FEMALE
}

fun main(args: Array<String>) {
    // 性别MALE
    var person = Person(Gender.MALE)
    person.name="Love"
    println("打印结果:${person.name}")
    //性别:FEMALE
    var person2 = Person(Gender.FEMALE)
    person2.name="Love"
    println("打印结果:${person2.name}")
}

使用默认 getter / setter 的属性,一定有幕后字段。对于 var 属性来说,只要 getter / setter 中有一个使用默认实现,就会生成幕后字段;

在自定义 getter / setter 中使用了 field 的属性

1.3 类

1.3.1类的访问修饰符

在java中 允许你创建任意类的子类并重写任意方法,除非显示的使用final关键字进行标注,对基类的修改会导致子类不正确的问题,也就是脆弱基类问题,在java中类和方法默认的是opne的。而kotlin中 默认都是final的。如果你想创建一个类的子类,需要使用open修饰符来表示这个类,此外还需要给每一个被重写的属性或者方法添加open修饰符号
下面是常用的修饰符

final修饰符                不能被重写              类中成员默认使用
open                       可以被重写              需要明确的表明
abstract                   必须被重写              只能在抽象类中使用,抽象成员不能有实现
override                 重写父类或者接口中的成员        如果没有使用final表明,重写的成员是默认开放的
在接口中不能使用final、open或者abstract。接口的成员始终是open的,不能将其声明为final 。

1.3.2 可见性修饰符

修饰符                                                     类成员                                           顶层声明
public                                                      所有地方可见                                 所有地方可见   
internal                                                    模块中可见                                    模块中可见
protected                                                  子类中可见
private                                                       类中可见                                      文件中可见      

1.3.2 类的构造方法

1.3.1.1 主构造方法

Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。主构造函数是类头的一部分:它跟在类名(与可选的类型参数)后。

class Person constructor(firstName: String) { ... }

如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。

class Person(firstName: String) { ... }

在kotlin中主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中。
在kotlin中初始化块与属性初始化器交织在一起,按照它们出现在类体中的顺序执行。
如果在构造方法中用val关键字来声明属性,就意味着相应的属性会用构造方法的参数来初始化。
如果所有的构造方法参数都有默认值,编译器会生成一个额外不带参数的构造放来使用所有的默认参数,

1.3.1.1 次构造方法

类也可以声明前缀有 constructor的次构造函数:

class Person {
  var children: MutableList<Person> = mutableListOf<Person>();
  constructor(parent: Person) {
      parent.children.add(this)
  }
}

如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数用 this 关键字

如果一个类没有主构造方法,那么每个从构造方法必须舒适化基类或者委托给另一个这样做了的构造方法

class myButton :View{
       constructor(ctx:Context):this(ctx,My_style){ //委托给另一个构造
       }
       constructor(ctx:Context,attr :AttributeSet):super(ctx,attr){
       }
}

1.4 数据类

几个kotlin中默认实现的方法

toString() 没什么特殊的
hashCode() 没什么特殊的
equals():

在java中 可以使用 == 比较基本数据类型和引用类型,如果应用在基本数据类型上,java中的 == 比较的是值,如果是在引用类型上 == 比较的是引用,
在kotlin中 == 运算符是比较两个对象的默认方式,检查的是对象是否相等,而不是比较引用,会编译称调用"equals"。如果想要进行引用比较,可以使用=== 运算符与java中比较对象引用效果相同。

1.4.1类委托:使用关键字 by

在设计大型面向对象系统的时候有一个常见的问题,就是有继承的实现导致的脆弱性。当扩展一个类并重写某些方法时,代码就变得依赖继承的类的实现细节。当系统不断的演进,并且基类的实现被修改或者新方法被添加进去的时候,关于基类的行为就会失效,这个就会发生错误,
kotlin的设计识别了这样的错误,并默认将类视作final 确保了只有那些可扩展的类可以被继承。看到其他关键字修饰类的时候就要注意修改的时候要和派生类兼容。
但是我们常常需要这种问题,类并么有被设计成可以扩展的,但是我们需要像类中添加一些行为。一个常用的实现方式以装饰器模式闻名。这中模式本质就是创建一个新类。实现与原始类一样的接口,并将原来的类的实例作为一个字段保存。与原始类似拥有同样的行为的方法不用被修改,只需要转发到原始类的实例就好。和java中的代理模式比较像
缺点是使用相当多的样板代码

// 创建接口
interface Base {   
    fun print()
}

// 实现此接口的被委托的类
class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

// 通过关键字 by 建立委托类
class Derived(b: Base) : Base by b

fun main(args: Array<String>) {
    val b = BaseImpl(10)
    Derived(b).print() // 输出 10
}

属性委托比较多,后面单独写

2 "object"关键字:

kotlin中的关键字object 使用的情况下比较多,object定义一个类并同时创建一个实例。

  • 对象声明是定义单例的一种方式
  • 伴生对象可以持用工厂方法和其他与这个类相关,但在调用时候并不依赖类实例的方法,他们的成员可以通过类名来访问。
  • 对象表达式来替代java的匿名内部类

2.1对象声明:创建单例易如反掌

在面向对象系统设计中 我们常见的情形就是只需要一个实例的类,在java中就是通过单例来实现,定义一个private构造方法,并使用静态字段持有这个类仅有的实例。
kotlin中使用对象声明功能来提供了最高级的语言支持,对象声明将类声明与该类的单一实例声明到了一起。

object Payroll {
	val allEm=arrayListOf<Preson>()
	fun calcula(){
		for(person int  allEm ){
        .................
        } 
	}
}

一个对象声明可以包含属性、方法、初始化语句块等声明,不允许有构造方法(不管是主构造还是从构造),和普通类的实例不同,对象声明在定义的时候就已经创建了,不需要在代码的其他地方调用构造方法,对象声明同样可以继承类和几口哦,但是你的实现并不包含任何状态的时候很好用,
同样可以在类中声明对象,这样的对象同样只有一个单一实例,他们在每个容器类的实例中并不同具有不同的实例。
在kotlin中的对象声明被编译称了通过静态字段来持有他的单一实例的类,这个字段的名字始终都是INSTANCE,因此java中使用看kotlin对象,可以通过访问静态INSTANCE字段。

2.2伴生对象:工厂方法和静态成员的地盘

kotlin中的类不能拥有静态成员,java的static关键字并不是kotlin语言的一部分,作为替代,kotlin依赖包级别函数(在大多数情形下能够替代java的静态方法)和对象声明(在其他情况下替代java的静态方法,同时还包括静态字段),kotlin中大多数情况还是推荐顶层函数但是顶层函数不能访问类的private成员,因此你需要写一个在没有类实例的情况下调用但是需要访问类内部的函数,可以将其写成那个类中的对象声明的成员,这种函数的一个例子就是工厂方法。

在类中定义对象可以使用特殊的关键字 标记 :companion。如果这样做了就可以通过直接访问容器类名称来访问这个对象的方法和属性的能力,不需要显示的指名对象的名称,
伴生对象是调用private构造方法的好地方,伴生对象可以访问类中所有的privare成员,包括private构造方法。是实现工厂模式的理想。下面我们看看例子

class User{
	val nickName :String
	constructor(email:String){
			nickname =email.substringBefore('@')
	}
	constructor(facebookAccountId:Int){
			nickNmae=getfaceBookName(facebookAccountId)
	}
}
、、使用工厂方法代替、
class User private constructor(val nickName:string){
		companion object{
				fun newSubscribingUser(email:String)=
						User(email.subStringBefore('@')
				fun newFaceBookUser(accountId :int)=
						User(getFacebookname(accountId))
      }
}

2.3对象表达式:改变写法的匿名内部类

object关键字不仅仅能用来声明单例式的对象。还能用来声明匿名对象。匿名对象替代了java中匿名内部类的用法,

window.addMouseListener(
		object:MouseAdapter(){//声明一个继承MouseAdapter的匿名对象  并重写了里面的方法
			 override fun mouseClicked(e:MouseEvent){
                  //
			}
			override fun mouseEntered(e:MouseEvent){
			//
			}
		}
)

出来去掉了对象的名字外,基本和对象声明相同,对象表达式声明了一个类并创建了该类的实例。但是没有给这个类或者实例分配一个名字。通常不需要,如果需要可以存到一个变量中

val listener =object:MouseAdapter(){
			override fun mouseClicked(e:MouseEvent){}
			override fun mouseEnter(e:mouseEvent){}
}

与java匿名内部类只能扩展一个类或者实现一个接口不同。kotlin的匿名对象可以实现多个接口或者不实现接口。与对象声明不同,匿名底下不是单例。每次对象表达式被执行都会创建一个新的对象实例。

和java的匿名类一样,在对象表达式中的代码可以访问创建它的函数中的变量,和java不同的是,访问没有被显示在final变量。还可以在表达式中修改标量的值,
下面是匿名对象访问局部变量的例子

fun countClicks(winow:window){
 var clickcount=0
 window.addMouseListener(onject:MouseAdapter(){
 			
			override fun mouseClicked(e:mouseEvent){
								clickCount++
			}
	}
}

先写这么多。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值