Kotlin基础 属性和接口(四)

属性和字段
属性声明
可以使用 var 关键字声明可变属性,或者用 val 关键字声明只读属性
Getters 和 Setters
声明一个属性的完整语法如下:

var <propertyName>: <PropertyType> [ = <property_initializer> ]
    <getter>
    <setter>

只读属性的声明语法和可变属性的声明语法相比有两点不同:
它以 val 而不是 var 开头
不允许 setter 函数

var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter
val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化
val inferredType = 1 // 类型为 Int 类型,默认实现 getter

filed字段表示当前域(备用字段)

  • 真正存储值的变量使用 field 指代
  • field 只在 get 和 set 方法中使用
    getter 叫读访问器,setter叫写访问器。
    val 声明的变量只有读访问器getter ,var声明的变量读写访问器都有。
    在java中,外部不能访问一个类的私有变量,必须提供一个setXXX方法和getXXX方法来访问,
    比如Java类Person,提供了getName()和setName()方法供外面方法私有变量name:
   public class Person{
       private String name;

       public String getName() {
           return name;
       }

       public void setName(String name) {
           this.name = name;
       }
   }

Kotlin的例子

fun main() {
    val tt = Address()
    tt.address = "teacher"
    println("-----")
    println(tt.address)

}

class Address{
    var city: String = "ChongQing"
    var address: String = ""
        get() {
            println("get invoke")
            return field  // 返回支撑地段,不能直接用属性,要不然会栈溢出 --->
            // get invoke
        }
        set(value) {
            println("set invoke")
            field = value
        }
}

如果你需要改变一个访问器的可见性或者给它添加注解,但又不想改变默认的实现,
那么你可以定义一个不带函数体的访问器:
var setterVisibility: String = “abc”// 非空类型必须初始化
private set // setter是私有的并且有默认的实现
var setterWithAnnotation: Any?
@Inject set // 用 Inject 注解 setter

延迟初始化属性
通常,那些被定义为拥有非空类型的属性,都需要在构造器中初始化.但有时候这并没有那么方便.
例如在单元测试中,属性应该通过依赖注入进行初始化, 或者通过一个 setup 方法进行初始化.
在这种条件下,你不能在构造器中提供一个非空的初始化语句,但是你仍然希望在访问这个属性的时候,
避免非空检查.为了处理这种情况,你可以为这个属性加上 lateinit 修饰符

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()
    }
}

这个修饰符只能够被用在类的 var 类型的可变属性定义中,不能用在构造方法中。
并且属性不能有自定义的 getter 和 setter访问器.这个属性的类型必须是非空的,
同样也不能为一个基本类型.
在一个lateinit的属性初始化前访问他,会导致一个特定异常,告诉你访问的时候值还没有初始化.

接口(interface)
可以包含抽象方法,以及方法的实现。和抽象类不同的是,
接口不能保存状态。可以有属性但必须是抽象的,或者提供访问器的实现。

interface MyInterface {
    fun bar()
    fun foo() {
        //函数体是可选的
    }
}

一个类或对象可以实现一个或多个接口

接口中的属性
可以在接口中申明属性。接口中的属性要么是抽象的,
要么提供访问器的实现。接口属性不可以有后备字段。而且访问器不可以引用它们。

解决重写冲突

interface A {
    fun foo() { print("A") }
    fun bar()
}

interface B {
    fun foo() { print("B") }
    fun bar() { print("bar") }
}

class C : A {
    override fun bar() { print("bar") }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }

    override fun bar() {
        super<B>.bar()
    }
}

A B 接口都有声明了 foo() 和 bar() 函数。它们都实现了 foo() 方法,
但只有 B 实现了 bar() ,bar() 在 A 中并没有声明它是抽象的,
这是因为在接口中如果函数没有函数体,那么默认是抽像的。
然而,如果我们从 A 和 B 派生 D,我们需要实现多个接口继承的所有方法,
并指明 D 应该如何实现它们。这一规则 既适用于继承单个实现(bar())
的方法也适用于继承多个实现(foo())的方法。

抽象类和接口的差异
语法层面上的区别
接口不能保存状态,可以有属性但必须是抽象的,而抽类型可以有属性。
一个类只能继承一个抽象类,而一个类却可以实现多个接口。

设计层面上的区别
1.抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 “是不是”的关系,而 接口 实现则是 “有没有”的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。

2.设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。

参考博客;链接:https://blog.csdn.net/io_field/article/details/52860971

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值