kotlin学习笔记(7)--类和对象

1.类定义

Kotlin 类可以包含:构造函数和初始化代码块、函数、属性、内部类、对象声明。

Kotlin 中使用关键字 class 声明类,后面紧跟类名:
class Test{ //类名是Test
      //类体
}
//定义空类
class Empty
//在类中构造成员函数
class Test{
      fun foo (){ ... } //成员函数
}

实例:把小数转换成分数
class Fraction(var number:Float){
     private var numerator:Long = 0 //分子
     private var denominator:Long = 0 //分母
     fun deal(){
         var array: List<String> = this.number.toString().split(".")
         numerator=array[1].toLong()
         denominator=Math.pow(10.0,array[1].length.toDouble()).toLong()
         var g=gcd(numerator,denominator);//求最大公约数
         println("${numerator/g}/${denominator/g}")
     }
     fun gcd(a:Long,b:Long):Long{
         //a,b算常量不能直接参与运算
         var c=a;
         var d=b;
         var r:Long=1L
         if(c<d){
             var temp=c
             c=d
             c=temp
         }
         r=c%d
         while(r!=0L){
             var temp=r
             r=d%r
             d=temp
         }
         return d
     }
}

fun main(args: Array<String>) {
    var myClass=Fraction(0.618f);
    myClass.deal()
}

2.类的属性

类的属性可以用关键字 var 声明为可变的,否则使用只读关键字 val 声明为不可变。
class Student{
      var name:String = ...
      var age:Int = ...
}

创建实例
val s = Student() //kotlin中没有new关键字

使用属性,只要用名称引用即可
s.name  //使用 . 来引用
Kotlin中的类可以有一个主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后:
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() = fileld.toUpperCase() //将变量赋值后转换为大写
      var num: Int=100
            get() = field //后端变量
            set(value){
                  if(value<10){ //如果传入的值小于 10 返回该值
                        field=value
                  }else{     
                        field=-1    //如果传入的值大于等于 10 返回-1
                  }
            }
       var height: Float = 145.4f
             private set
}
fun main(args: Array<String>){
        var person: Person = Person()
        person.lastName = "wang"
        println("lastName:${person.lastName}")
        person.num = 9
        println("num:${person.num}")
        person.num = 20
        println("num:${person.num}")
}
输出:lastName:WANG
           num:9
           num:-1
Kotlin 中类不能有字段。提供了 Backing Fields(后端变量) 机制,备用字段使用field关键字声明,field 关键词只能用于属性的访问器,如以上实例;
非空属性必须在定义的时候初始化,kotlin提供了一种可以延迟初始化的方案,使用 lateinit 关键字描述属性:
public class MyTest {
        lateinit var subject: TestSubject
        @SetUp fun setup() {
               subject = TestSubject()
         }
        @Test fun test() {
               subject.method() // dereference directly
         }
}

3.类的修饰符

类的修饰符包括 classModifier 和_accessModifier_:

classModifier: 类属性修饰符,标示类本身特性
abstract       //抽象类
final             //类不可继承,默认属性
enum           //枚举类
open            //类可继承,默认是final
annotation   //注解类

accessModifier: 访问权限修饰符
private        //仅在同一个文件中可见
protected    //同一个文件和它所有子类中可见
public          //都可见,默认类型
internal       //同一个模块中可见

实例:
// 文件名:example.kt
package foo
private fun foo() {} // 在 example.kt 内可见
public var bar: Int = 5 // 该属性随处可见
internal val baz = 6 // 相同模块内可见

java中的访问修饰符,默认default,kotlin没有

 publicprotecteddefaultprivate
同一类中的成员
同一包中的成员
不同包中的子类
不同包中的非子类

4.构造器详解

主构造器:不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀。
class Person constructor(firstName: String){
       init{
            println("firstName is $firstName")
       }
}
实例:
class Person public constructor(name: String,age: Int)/*主构造函数,和它参数*/{
    var name:String  //属性
    var age :Int
    init {  //初始化器,主构造函数的函数体,能使用主构造函数的参数
        this.name = name
        this.age = age
    }

    //成员函数
    fun say(){
        println("I am ${name} and I was ${age} years old")
    }
}
注意:主构造器的参数可以在初始化代码段中使用,也可以在类主体n定义的属性初始化代码中使用。 一种简洁语法,可以通过主构造器来定义属性并初始化属性值(可以是var或val):
//这种var或val修饰参数可以直接初始化
class People(val firstName: String, val lastName: String){
      //...
}
如果构造器有注解,或者有可见度修饰符,这时constructor关键字是必须的,注解和修饰符要放在它之前。
实例:
class Person constructor(val name: String){
      var age: Int = 18
      var country: String = "CN"
      var pname = name

       init{
           println("初始化")
       }
       fun printTest(){
           println("我是类的函数")
       }
}
fun main(args: Array<String>){
       val person = Person("wang")
       println(person.pname)
       prinln(person.age)
       println(person.country)
       person.printTest()
}
输出:初始化
           wang
           18
           CN
           我是类的函数

次构造器:类也可以有二级构造函数,需要加前缀 constructor;
如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:
class Person(val name: String) {
        constructor (name: String, age:Int) : this(name) {
                   // 初始化...
         }
}
如果一个非抽象类没有声明构造函数(主构造函数或次构造函数),它会产生一个没有参数的构造函数。构造函数是 public 。如果你不想你的类有公共的构造函数,你就得声明一个空的主构造函数:
class DontCreateMe private constructor () { }
实例:
class Person constructor(val name: String){
      var age: Int = 18
      var country: String = "CN"
      var pname = name

       init{
           println("初始化")
       }
       //次构造函数
      constructor(name: String, sex: String): this(name){
            println("性别:$sex")
      }
       fun printTest(){
           println("我是类的函数")
       }
}
fun main(args: Array<String>){
       val person = Person("wang", "男")
       println(person.pname)
       prinln(person.age)
       println(person.country)
       person.printTest()
}
输出:初始化
           性别:男
           wang
           18
           CN
           我是类的函数

5.抽象类

抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的。抽象成员在类中不存在具体的实现。
注意:无需对抽象类或抽象成员标注open注解。
open class Base {
         open fun f() {}
}
abstract class Derived : Base() {
         override abstract fun f()
}

6.嵌套类

class Outer{  //外部类
      private val bar: Int = 1
      class Nested{  //嵌套类,不支持外部类的引用
            fun foo() = 2
      }
}
fun main(args: Array<String>){
      val demo = Outer.Nested().foo() //调用格式:外部类.嵌套类.嵌套类方法/属性
      println(demo)  //输出:2
}

7.内部类

内部类使用 inner 关键字来表示。

内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
class Outer{  //外部类
      private val bar: Int = 1
      var v = "成员属性"
     /**嵌套内部类**/
      inner class Inner{ 
            fun foo() = bar //访问外部类成员
            fun innerTest(){
                 var o = this@Outer //获得外部类成员变量
                 println("内部类可以引用外部类的成员,例如:" + o.v)
            }
      }
}
fun main(args: Array<String>){
      val demo = Outer.Inner.foo()
      println(demo)  //输出:1
      val demo2 = Outer().Inner().innerTest()
      println(demo2) //输出:内部类可以引用外部类的成员,例如:成员属性
}
为了消除歧义,要访问来自外部作用域的 this,我们使用this@label,其中 @label 是一个 代指 this 来源的标签。

8.匿名内部类

使用对象表达式来创建匿名内部类:

class Test{
       var v = "成员属性"
       fun setInterFace(test: TestInterFace){
              test.test()
       }
}
/**
 * 定义接口
 */
interface TestInterFace {
       fun test()
}
fun main(args: Array<String>) {
        var test = Test()
        /**
         * 采用对象表达式来创建接口对象,即匿名内部类的实例。
         */
       test.setInterFace(object : TestInterFace {
               override fun test() {
                       println("对象表达式创建匿名内部类的实例")
               }
       })
}

9.数据类

Kotlin 可以创建一个只包含数据的类,关键字为 data
data class User(val name:String, val age:Int)
编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
equals() / hashCode()
toString() 格式如“User(name="wang", age=18)”
componentN() functions对应于属性,按声明顺序排列
copy()函数

如果这些函数在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。
为了保证生成代码的一致性以及有意义,数据类需要满足以下条件:
1.主构造函数至少包括一个参数
2.所有的主构造函数的参数必须标识为 val或var ;
3.数据类不可以声明为不可以声明为abstract,open,sealed或inner
4.数据类不能继承其他类但可以实现接口

复制:复制使用 copy() 函数,我们可以使用该函数复制对象并修改部分属性, 对于上文的 User 类,其实现会类似下面这样:
fun copy(name: String = this.name, age: Int=this.age)=User(name, age)

实例:
data class User(val name:String, val age:Int)
fun main(args: Array<String>){
     val jack=User(name="jack", age=18)
     val olderJack = jack.copy(age = 2)
     println(jack)
     println(olderJack)
}
输出:User(name=Jack, age=1)
           User(name=Jack, age=2)

数据类以及解构声明:组件函数允许数据类在解构声明中使用
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"

10.密封类

密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。

声明一个密封类,使用 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 NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) {
         is Const -> expr.number
         is Sum -> eval(expr.e1) + eval(expr.e2)
         NotANumber -> Double.NaN
}
使用密封类的关键好处在于使用 when 表达式 的时候,如果能够 验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值