类、对象和接口

1、定义类继承结构


1.1 kotlin中的接口

声明接口

interface Clickable{
    fun click()
}

声明了一个只有一个抽象方法的接口,和java中一样,实现这个接口的类要提供这个抽象方法的具体实现。

实现接口

class Button:Clickable{
    override fun click()=println("I was clicked")//override修饰符是强制要求的
}

在kotlin中“:”相当于java中的extends或implements。

在接口中定义一个带方法体的方法

interface Clickable{
    fun click()
    fun showOff()=println("I'm clickable!")//带默认实现的方法
}

我们实现这个接口的类中要为click()方法提供实现。而对于showOff()方法则有些无所谓了,你可以直接调用,也可以重新定义。

1.2 open、final和abstract修饰符:默认为final

kotlin中类和方法默认都是final的,如果想允许一个类有子类,那么需要用open修饰。

open class RichButton:Clickable{//这个类是open的:其他类可以继承它
    fun disable(){}//这个函数是默认是final修饰的,虽然没有写出来。不能再子类中重写它
    open fun animate(){}//用open修饰,可以再子类中重写
    override fun click(){}//重写了一个函数
}

上面的这个click(){}函数是重写的,它默认是open,可以被RichButton的子类再次重写,如果我们不想让它重写了,那么可以用final修饰它。

open class RichButton:Clickable{
    final override fun click(){}//这样子类就不能重写了
}

抽象类
kotlin中同样有抽象类,也是用abstract关键字,这种类不能被实例化。抽象类的成员始终是open的,我们不需要明确用open修饰。

abstract class Animated{
    abstract fun animate()//抽象函数,子类必须重写它
    open fun stopAnimating(){}
    fun animateTwice(){}//open修饰符可写可不写
}
修饰符 相关成员 备注
final 不能被重写 在类中被默认使用
open 可以被重写 必须要标明
abstract 必须被重写 只能在抽象类中使用,抽象成员不能有实现
override 重写父类或接口中的成员 如果没有使用final标明,重写的成员默认是open的

1.3 可见性修饰符:默认为public

修饰符 类成员 顶层声明
public 所有地方可见 所有地方可见
internal 模块中可见 模块中可见
protected 子类中可见 -
private 类中可见 文件中可见
  • kotlin中protected成员只在类和它的子类中可见,不可以从同一包内访问一个protected成员。
  • 外部类不能看到内部类中private中的成员。

1.4 内部类和嵌套类:默认是嵌套类

类A在另一个类B中声明 在java中 在kotlin中
嵌套类(不存储外部类的引用) static class A class A
内部类(存储外部类的引用) class A inner class A

1.5 密封类:定义受限的类继承结构

密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举
类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。
声明一个密封类,使用 sealed 修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中。
使用密封类的关键好处在于使用 when 表达式 的时候,如果能够 验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了。

fun eval(expr: Expr): Double = when(expr) {
    is Expr.Const -> expr.number
    is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
    Expr.NotANumber -> Double.NaN
    // 不再需要 `else` 子句,因为我们已经覆盖了所有的情况
}

2、声明一个带非默认构造方法或属性的类


2.1 初始化类:主构造方法和初始化语句块

我们先声明一个简单类

class User(val nickname:String)

括号里面的语句块叫作主构造方法。
它又两个目的:①标明构造方法的参数②定义使用这些参数初始化的属性
它相当于

class User constructor(nickname:String){//带一个参数的主构造方法
    val nickname:String
    init{//初始化语句块
        this.nickname=nickname
        }
}

如果没有给类声明任何构造方法,将会生成一个不带参数的默认构造方法。

2.2 构造方法:用不同的方式来初始化父类

kotlin中的重载

open class View{
    constructor(ctx:Context){
    }
    constructor(ctx:Context,attr:AttributeSet){
    }
}

这个类没有声明一个主构造方法,它声明的是两个从构造方法。

2.3 实现在接口中声明的属性

java中不可以声明抽象的成员变量,在kotlin中可以。

interface User{
    val nickname:String
}

class PrivateUser(override val nickname:String):User

除了抽象属性声明外,接口还可以包含具有getter和setter属性

2.4 通过getter或setter访问支持字段

实现一个既可以存储值又可以在值被访问和修改时提供额外逻辑的属性。

class User(val name:String){
    var address:String="unspecified"
    set(value:String){
        println("""
            Address was changed for $name:
            "$field"->"$value".""".trimIndent())//读取支持字段的值
            field=value//更新支持字段的值
        }
}

2.5 修改访问器的可见性

声明一个具有private setter的属性

class LengthCounter{
    val counter:Int=0
    private set
    fun addWord(word:String){
        counter+=word.length
    }
}

3、编译器生成的方法:数据类和类委托


3.1 通用对象方法

toString()
equals()
hashCode()

3.2 数据类

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() 函数,我们可以使用该函数复制对象并修改部分属性, 对于上文的 User 类,其实现会类似下面这样:
    fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
    使用 copy 类复制 User 数据类,并修改 age 属性:
data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
    val jack = User(name = "Jack", age = 1)
    val olderJack = jack.copy(age = 2)
    println(jack)
    println(olderJack)

}

3.3 类委托:使用"by"关键字

委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
类的委托即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。

以下实例中派生类 Derived 继承了接口 Base 所有方法,并且委托一个传入的 Base 类的对象来执行这些方法。

// 创建接口
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
}

在 Derived 声明中,by 子句表示,将 b 保存在 Derived 的对象实例内部,而且编译器将会生成继承自 Base 接口的所有方法, 并将调用转发给 b

Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 by 实现委托

4、"object"关键字:将声明一个类与创建一个实例结合起来


4.1 对象声明

kotlin中使用object关键字来声明一个对象
通过对象声明获取一个单例很方便

object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        // ……
    }

    val allDataProviders: Collection<DataProvider>
        get() = // ……
}

当对象声明在另一个类的内部时,这个对象并不能通过外部类的实例访问到该对象,而只能通过类名来访问,同样该对象也不能直接访问到外部类的方法和变量。

4.2 伴生对象

类内部的对象声明可以用 companion 关键字标记,这样它就与外部类关联在一起,我们就可以直接通过外部类访问到对象的内部元素。

class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}

val instance = MyClass.create()   // 访问到对象的内部元素

我们可以省略掉该对象的对象名,然后使用 Companion 替代需要声明的对象名。
一个类里面只能声明一个内部关联对象,即关键字 companion 只能使用一次。

4.3 对象表达式

通过对象表达式实现一个匿名内部类的对象用于方法的参数中:

window.addMouseListener(object : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        // ...
    }
    override fun mouseEntered(e: MouseEvent) {
        // ...
    }
})

与java匿名内部类只能扩展一个类或实现一个接口不同,kotlin的匿名对象可以实现多个接口或者不实现接口。

原文地址:https://www.cnblogs.com/dearnotes/p/12292976.html

时间: 2024-11-09 05:52:09

类、对象和接口的相关文章

C# 开发 &mdash;&mdash; 数组类对象接口

数组类型是从抽象基类 Array 派生的引用类型,通过new运算符创建数组并将数组元素初始化为他们的默认值 一维数组 type[] arrayname; 数组的长度不是声明的一部分,而且数组必须在访问前初始化. foreach 语句声明一个迭代变量 -- 是数组的每个元素的只读副本 二维数组 type[,]  arrayName; int[,] arr = new int[2,2]{{1,2},{3,4}}; 可使用数组的Rank属性和GetUpperBound方法获取数组的行数和列数,然后遍历

JAVA基础学习之流的简述及演示案例、用缓冲区方法buffer读写文件、File类对象的使用、Serializable标记接口(6)

1.流的简述及演示案例输入流和输出流相对于内存设备而言.将外设中的数据读取到内存中:输入将内存的数写入到外设中:输出.字符流的由来:其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表.获取对应的文字.在对这个文字进行操作.简单说:字节流+编码表字节流的两个顶层父类:1,InputStream 2,OutputStream.字符流的两个顶层父类:1,Reader 2,Writer String filePath = "F:\\demo.txt"; FileWriter f

WPFS数据绑定(要是后台类对象的属性值发生改变,通知在“client界面与之绑定的控件值”也发生改变须要实现INotitypropertyChanged接口)

WPFS数据绑定(要是后台类对象的属性值发生改变,通知在"client界面与之绑定的控件值"也发生改变须要实现INotitypropertyChanged接口) MainWindow.xaml <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="

PHP 面向对对象基础(接口,类)

介绍PHP面向对象的基础知识 1. 接口的定义interface ,类定义class,类支持abstract和final修饰符,abstract修饰为抽象类,抽象类 不支持直接实例化,final修饰的类/方法不能被继承/方法重写.2. 接口的实现通过implements,类继承extends interface IShape{ function draw_core(); }; class PathShape implements IShape{ public function draw_core

父类的引用指向子类的对象或者接口指向实现类均是可以的

父类的引用指向子类的对象或者接口指向实现类均是可以的. 例如(下图为父类的引用指向子类的对象) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Father {         public void pri() {             System.out.println("father");         } } public class Sun   extends  Father{       public void 

Kotlin基础(三)类、对象和接口

类.对象和接口 一.定义类的继承结构 一)Kotlin中的接口 Kotlin的接口与Java8中相似,它们可以包含抽象方法的定义以及非抽象方法的实现,但它们不能包含任何状态. 1 interface Clickable{ 2 fun click() 3 fun showoff()=println("It's show time!") 4 } 5 6 interface Focusable{ 7 fun setFocus(b: Boolean)= 8 println("I ${

设计模式之适配器模式 adapter 适配器模式分类概念角色详解 类适配器 对象适配器 接口适配器 双向适配器

现实世界中的适配器模型 先来看下来几个图片,截图自淘宝 上图为港版的插头与港版的插座 上图为插座适配器卖家的描述图 上图为适配后的结果 现实世界中适配器模式 角色分类 这就是适配器模式在电源插座上的应用 我们看下在插座适配器中的几个重要角色 可以看得出来,大陆和港版插座面板,都是作为电源的角色,他们的功能是相似的或者说相近的 插头要使用插座,进而接通电流 现实世界到代码的转换 电源插座代码示例 港版插座面板 package adapter; /**目标角色 Target 接口 * 香港地区使用的

第四章(类、对象和接口)

Kotlin的类和接口与Java的类和接口还是有一点区别的.例如:接口可以包含属性声明.与Java不同,Kotlin的声明默认是 final?和 public?的.此外,嵌套的类默认并不是内部类:它们并没有包含对其外部类的隐式引用. Kotlin中的接口 Kotlin的接口与Java 8中的相似:它们可以包含抽象方法的定义以及非抽象方法的实现.与Java 8不同的是,Java 8中需要你在这样的实现上标注default关键字,而Kotlin没有特殊的注解:只需要提供一个方法体.示例: inter

C++实现根据类名动态生成类对象

在开发后台服务的过程中,我们常常需要从数据库中取数据,并将数据缓存在本地中,另外,我们的服务还需要有更新数据的能力:包括定时的主动更新以及数据库数据更新时服务收到通知的被动更新. 之前在需要用到以上功能的时候,模仿着组内通用的数据Cache部分的代码来写,十分方便,基本上只需要自己写两个类:一个是取数据并缓存数据的类XXXData,一个是扇出数据的类XXXFetcher. 在需要使用数据的时候,通过: FetcherFactory::getFetcher<XXXFetcher>() 即可获取一

面试题:Java中对象序列化接口(Serializable)的意义

Serializable接口是一个里面什么都没有的接口 它的源代码是public interface Serializable{},即什么都没有. 如果一个接口里面什么内容都没有,那么这个接口是一个标识接口,比如,一个学生遇到一个问题,排错排了几天也没解决,此时,她举手了(示意我去帮他解决),然后我过去,帮他解决了,那么这个举手其实就是一个标识,自己不能解决的问题标示我去帮他解决,在Java中的这个Serializable接口是给JVM看的,告诉JVM,我不做这个类的序列化了,你(JVM)给我序