kotlin学习(一)基本概念、数据对象类型、控制流程、空值检验、类与接口

news2024/11/19 15:35:10

文章目录

  • 认识Kotlin
    • 跨平台特性
      • 语言类型
      • java的语言类型
      • kotlin的运行原理
    • hello world
  • 基本概念
    • 程序入口
    • 数据与对象类型
    • == 和 ===
      • 显式数字转换
      • 浮点类型
      • 位运算
      • Any
      • Unit
      • Nothing
    • 声明变量
    • 只读变量 val与可变变量var
      • 查看Kotlin字节码
    • fun(方法 / 函数)
      • 函数参数默认值
    • class(类)
    • $(字符串模板)
  • 控制流程
    • if-else(条件表达式)
    • 循环
      • for
      • while
      • when表达式
  • 异常
    • 异常类Throwable
    • null值和null值检查
      • 安全调用运算符`?.`
      • Elvis运算符`?:`
      • 按照转换`as?`
      • 非空断言`!!`
      • let函数
      • lateinit 延迟初始化属性
      • 可空类型拓展
      • 类型参数的可空性
    • is:类型检查和自动转换
  • 类和对象
    • 继承
    • 构造
      • 主构造
      • 次构造
    • 权限修饰符
    • 数据类和单例类
      • 数据类
      • 单例类
    • 枚举类
  • 接口
    • 接口的继承
      • 解决覆盖冲突
    • 函数式接口(SAM)接口

认识Kotlin

在这里插入图片描述
kotlin与java语言一样,编译成字节码后,在JVM虚拟机中运行。kotlin语言的特性依赖于kotlin的语法编译器。与kotlin类似的语言还有:

  • Scala:大数据领域开发语言
  • Groovy:基于 JVM 平台的动态脚本语言,在安卓开发中用来编写依赖关系、插件等

2019年谷歌宣布Kotlin成为安卓第一开发语言,安卓程序员由java转Kotlin已经迫在眉睫。

跨平台特性

语言类型

语言分为解释型和编译型:

  • 编译型的语言,编译器会将源代码一次项编译成二进制文件,计算机可以直接执行,例如C和C++
    • 优点:一次编译即可运行,运行期不需要编译,效率高。
    • 缺点:不同操作系统需要不同的机器码,且修改代码需要逐个模块重新编译。
  • 解释型的语言,程序运行时,解释器会将源码一行一行实时解析成二进制并且放在对应的虚拟机中执行,例如JS、Python
    • 优点:平台兼容性好,安装对应的虚拟机即可运行。
    • 缺点:运行时需要解释执行,效率低。

java的语言类型

java准确来说属于半编译半解释的混合型语言,但更偏向于解释型。

Java通过编译器javac先将源程序编译成与平台无关的Java字节码(.class),再由JVM解释执行字节码。

kotlin的运行原理

JVM只负责对字节码文件.class解释执行,而不管字节码文件是怎么产生的,Kotlin就是此原理,运行前会先编译成class,再供java虚拟机运行。

Kotlin不仅能够,还能脱离虚拟机层,直接编译成可以在Windows、Linux和MacOS平台上运行的原生二进制代码,被称为Android领域的Swift

hello world

Kotlin应用入口是主函数main,主函数可以不带任何参数,没有返回值。

fun main() {
    var str:String = "Hello World"
    println(str)
}

基本概念

程序入口

可以没有参数,也可以带参数列表,但程序入口是没有返回值的。

fun main() {
}

fun main(args: Array<String>) {
}

数据与对象类型

kotlin中取消了Java中原有的基本数据类型,全部使用对象类型。
在这里插入图片描述
在这里插入图片描述

== 和 ===

kotlin提供了两种方式用于对象的比较

  • 比较对象的结构是否相等( == 或者equals
  • 比较对象的引用是否相等===
fun main() {
//sampleStart
    val a: Int = 100
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a

    val b: Int = 10000
    val boxedB: Int? = b
    val anotherBoxedB: Int? = b

    println(boxedA === anotherBoxedA) // true
    println(boxedB === anotherBoxedB) // false
//sampleEnd
}

由于 JVM 对 -128 到 127 的整数(Integer)应用了内存优化,因此,a 的所有可空引用实际上都是同一对象。但是没有对 b 应用内存优化,所以它们是不同对象(===的结果为false)。

另一方面,它们的内容仍然相等:

fun main() {
//sampleStart
    val b: Int = 10000
    println(b == b) // 输出“true”
    val boxedB: Int? = b
    val anotherBoxedB: Int? = b
    println(boxedB == anotherBoxedB) // 输出“true”
//sampleEnd
}

显式数字转换

kotlin中较小的类型不能隐式转换为较大的类型,这意味着把Byte类型赋值给一个Int变量必须显式转换:

fun main() {
	val b : Byte = 1

	val i1: Int = b.toInt();
}

所有数字类型都支持转换为其他类型:

  • toByte(): Byte
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double

很多情况都不需要显式类型转换,因为类型会从上下文推断出来, 而算术运算会有重载做适当转换,例如:

val l = 1L + 3 // Long + Int => Long

浮点类型

Kotlin 提供了浮点类型 Float 与 Double 类型,这两个类型的大小不同,并为两种不同精度的浮点数提供存储:
在这里插入图片描述

  1. 对于以小数初始化的变量,编译器会推断为Double类型:
val pi = 3.14  // Double
// val one: Double = 1 // 错误:类型不匹配
val oneDouble = 1.0 //Double
  1. 如需将一个值显式指定为Float类型,请添加f或者F后缀,如果这样的值包含多于6~7位十进制数,则会将其舍入:
val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float,实际值为 2.7182817
  1. 与其他语言不同,Kotlin中的数字没有隐式拓宽转换,例如具有Double 参数的函数只能对 Double 值调用,而不能对 Float、 Int 或者其他数字值调用
fun main() {
    fun printDouble(d: Double) { print(d) }

    val i = 1    
    val d = 1.0
    val f = 1.0f 

    printDouble(d)
//    printDouble(i) // 错误:类型不匹配
//    printDouble(f) // 错误:类型不匹配
}

位运算

Kotlin对于整数类型IntLong提供了一组位运算。

  • shl(bits) – 有符号左移(shift left)
  • shr(bits) – 有符号右移(shift right)
  • ushr(bits) – 无符号右移(unsigned shift right)
  • and(bits) – 位与
  • or(bits) – 位或
  • xor(bits) – 位异或
  • inv() – 位非(inverse)

Any

Any是非空类型的根类型,与Object作为Java类层级结构的顶层类似,Any类型是Kotlin中所有非空类型的超类。在Kotlin函数中使用Any,它会编译成Java字节码中的Object。

Any?类型是Kotlin所有类型的根类
在Kotlin类型层级结构的最底层是Nothing类型。类型结构图如下:
在这里插入图片描述

Unit

Unit的作用类似于Java中的void,但是void的含义是没有返回值,kotlin面对对象更加彻底,没有返回值的返回值也应该是一种对象,于是就有了Unit,这个概念
如果没有用return明确指定返回值,那么编译器会自动加上返回Unit

fun returnUnit() : Unit {
	return Unit
}

Nothing

Unit的含义这个函数是没有返回值的。
Nothing,则是提示开发者这个函数不会返回,例如它可能是抛异常,不会返回,或者拥有无限循环的函数。

作用1:抛异常的函数的返回值

fun throwOnNameNull() : Nothing {
	throw NullPointerException("姓名不能为空!")
}

除了上边抛异常这个用法,==Nothing对象还是所有对象类型的共同子类型。==这样一来,实际上Nothing是多重继承的,即Kotlin中扩展了继承规则,类不允许多重继承,Nothing除外,因为Nothing的构造器是私有的,它不会有任何实例对象,所以他的多重继承是不会带来任何实际风险的。

基于Nothing是所有类型的子类,可以用它结合泛型,构建通用的空List,如下:

在这里插入图片描述
作用2:作为泛型变量的通用的、空白的临时填充

作用3:Nothing使得kotlin在语法层面更加完整。

在kotlin的下层逻辑中,throw是有返回值的,它的返回值类型就是Nothing
因此下边的写法是合法的:

val nothing: Nothing = throw RuntimeException("抛异常!"

因为Nothing是所有类型的子类,所有下面的写法才能够成立:

var _name: String? = null
val name: String = _name ?:throw NullPointerException("_name 运行时不能为空!")

声明变量

kotlin使用关键字var声明可变变量,变量名与变量类型间用冒号:隔开,这种脚本语言与Python一样,不需要;结尾。

在这里插入图片描述
var:可变变量,对应Java的非final变量

var b = 1

val:不可变变量,对应Java的final变量

val a = 1

上边的声明过程中并未使用冒号:指明类型,因为Kotlin存在类推导机制,上述的ab会根据赋值结果默认视为Int

只读变量 val与可变变量var

  • var:表示可修改变量
  • val:表示只读变量

主要是非final与final的区别。

查看Kotlin字节码

在这里插入图片描述

fun(方法 / 函数)

fun 关键字用于声明方法:
下面是一个接收2个Int参数,返回Int的方法。

fun sum(a: Int, b: Int): Int {
	return a + b;
}

方法主体可以是一个表达式,它的返回值可以被推断出来:

fun sum (a: Int, b: Int) = a + b 

方法可以没有返回值,或者说返回一个无意义的值(Unit)

fun printSum(a: Int, b:Int): Unit {
	println("sum of $a and $b is ${a + b}")
}

其中Unit也可以忽略不写;

fun printSum(a: Int, b:Int) {
	println("sum of $a and $b is ${a + b}")
}

函数参数默认值

Kotlin支持函数存在默认值,使用如下:

fun main() {
	myPrint(1)
	myPrint(1, "lalala")
}

fun myPrint(value : Int, str : String = "hello") {
	println("num is $value, str is $str")
}

若value想为默认值,则会保存,因为在使用时传入第一个参数必须是Int型。传入字符串类型会不匹配:

fun main() {
    myPrint("zjm")//报错
}

fun myPrint(value: Int = 100, str: String) {
    println("num is $value, str is $str")
}

Kotlin的函数传参支持与Python一样的键值对传参,从而可以改变参数的顺序:

fun main() {
    myPrint(str = "zjm") //正确调用
}

fun myPrint(value: Int = 100, str: String) {
    println("num is $value, str is $str")
}

class(类)

定义一个类
类的属性可以放在定义中或者类里边,比如下边:

class Rectangle(var height: Double, var length: Double) {
	var perimeter = (height + length) * 2
}

var rectangle = Rectangle(5.0, 2.0)
println("The perimeter is ${rectangle.perimeter}")

对于kotlin 1.4.30版本,类的继承用冒号:来表示,类默认都是final的,不可继承,为了使得类可以被继承,用open关键字,放在class前面:

open class Rectangle(var height: Double, var length: Double) {
	var perimeter = (height + length) * 2
}

$(字符串模板)

使用$可以帮助定义字符串模板,转译字符串变量。
直接使用变量用$,要使用表达式,需要大括号${}

下边是例子:

var a = 1
val S1 = "a is $a"
println(S1)
a = 2;
val S2 = "${S1.replace("is", "was")}, but now is $a"
println(S2)

结果:

a is 1
a was 1, but now is 2

控制流程

if-else(条件表达式)

fun getMax(a: Int, b: Int): Int {
	if (a > b) {
		return a
	} else {
		return b
	}
}

上边的if也可以写成一个表达式,类似三目运算符:

fun getMax(a: Int, b: Int) = if (a > b) a else b

循环

for

  1. 使用in 关键字
  2. 使用下标
val items = listof("apple", "banana", "kiwifruit")
for (item in items) {
	println(item)
}

for (index in item.indices) {
	println("item at $index is ${items[index]}")
}
// 闭区间打印[1, 3]
// 输出 1,2,3
for (i in 1..3) {
	println(i)
}
// 从7到0,降序,步长为2
// 输出 7,5,3,1
for (i in 7 downTo 0 step 2) {
	println(i)
}

for- in 的区间默认是双闭区间,如果想使用左闭右开呢,需要借助关键字until

fun main() {
	for (i in 0 until 10) {
		
	}
}

while

val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
    println("item at $index is ${items[index++]}")
}

结果:

item at 0 is apple
item at 1 is banana
item at 2 is kiwifruit

when表达式

when表达式看起来有点像java中的swich case。

fun describe(obj: Any): String = 
	when (obj) {
		1 -> "One"
		"Hello" -> "Greeting"
		is Long -> "Long"
		!is String -> "Not a String"
		else -> "Unknown"
	}

异常

异常类Throwable

Kotlin中所有异常类继承自Throwable类,使用throw表达式抛出异常:

fun main() {
//sampleStart
    throw Exception("Hi There!")
//sampleEnd
}

使用try … catch 表达式捕获异常:

try {
    // 一些代码
} catch (e: SomeException) {
    // 处理程序
} finally {
    // 可选的 finally 块
}

可以有零到多个 catch 块,finally 块可以省略。 但是 catch 与 finally 块至少需有一个。

null值和null值检查

可以为null的变量,后边需要一个问号?
例如下边这个方法,返回Int或者null

fun parseInt(str: String) : Int? {
	...
}

安全调用运算符?.

该运算符运行你把一次null检查和一次方法调用合并为一个操作。例如

s?.toUpperCase()

等同于下边的写法:

if (s!=null) {
	s.toUpperCase
} else {
	null
}

安全调用不光可以调用方法,也能用来访问属性。

如果你的对象中有多个可空类型的属性,通常可以在同一个表达式中方便地使用多个安全调用。使用该运算符,不需要额外的检查,就可以在一行代码中访问到更深层次的属性。如下:

class Address(val streetAddress:String,val code:Int,val city:String)
class Company(val name:String,val address: Address?)
class Person(val name:String,val company: Company?)

fun Person.cityName():String{
    val city = this.company?.address?.city
    return if (city != null) city else "unKnown"
}

Elvis运算符?:

Elvis 运算符接收两个运算数,如果第一个运算数不为 null,运算结果就是第一个运算数,否则运算结果就是第二个运算数。
例如下边,如果s不为空,则str为s,否则str为空字符串对象

fun foo(s: String) {
	val str: String = s?:""
}

Elvis运算符经常和安全调用运算符一起使用,用一个值代替对null对象调用方法时返回的null。下面就对之前的代码进行简化:

fun Person.cityName():String {
	val city = this.company?.address?.city
	return city ?: "unKnown"
}

在 kotlin 中有一种场景 Elvis 运算符非常适合,像 return 和 throw 这样的操作其实是表达式,因此可以把它们写在 Elvis 运算符的右边。当运算符左边的值为null,函数立即抛出一个异常。

fun printPersonName(person : Person) {
	val name = person.name? : throw IllegalArgumetException("no name")
	println(name)
}

按照转换as?

as是用来转换类型的Kotlin运算符,和java类型一样,如果被转换的值不是你试图转换的类型。就会抛出ClassCastException异常。

as? 运算符尝试把值转换成指定的类型,如果值不是合适的类型则返回 null。我们可以将该运算符与 Elvis 结合使用,如实现 Person 类的 equals 方法

class Person(val name:String, val company: Company?) {
	override fun equals(other: Any?): Boolean {
		val otherPerson = other as? Person ?: return false
		return otherPerson.name && other.company == company
	}
}

非空断言!!

非空断言是kotlin 提供的最简单直接的处理可空类型值的工具。使用双感叹号表示,可以把任意值转换成非空类型。如果对 null 做非空断言,则会抛出异常。

fun ignoreNull(s:String){
    val notNull = s!!
    println(notNull.length)
}

如果该函数中s为null,则会在运行时抛出异常。抛出异常的位置是非空断言所在的那一行,而不接下来试图使用值的那一行。

因为异常调用栈跟踪的信息只表明异常发生在哪一行,为了让跟踪信息更加明确,最后避免在同一行中使用多个!!断言。

let函数

let函数可以让处理可空表达式变的更加容易和安全调用运算符一起使用,它允许你对表达式求值,检查求值结果是否为null,并把结果保存为一个变量,所有的这些操作都在同一个简单的表达式中。

let函数所作的事情就是把一个调用它的对象变成lambda表达式的参数,结合安全调用语法,能有效地把调用let函数的可空对象转变为非空类型,换言之,安全调用的let只在表达式不为null时才执行lambda。

s?.let { 
    print(s.length)
}

如果要实现上边的效果,Java代码如下:

public static final void ignoreNull(@Nullable String s) {
	if (s != null) {
		System.out.print(s.length());
	}
}

lateinit 延迟初始化属性

kotlin通常要求在构造方法中初始化所有属性,如果某个属性是非空属性,就必须提供非空的初始化值,否则就必须使用可空类型,如果这样做,该属性每一次访问都需要null检查或者!!断言。

class MyService {
	fun performAction():String = "foo"
}

class MyTest{
	private var myService:MyService?=null
	
	fun test() {
		myService!!.performAction()
	}
}

可以使用lateinit来延迟初始化myService,延迟初始化 的属性都是var,尽管是非空类型,但也不需要在构造方法中去初始化它。

class MyService{
    fun performAction():String = "foo"
}

class MyTest{
    // 声明一个不需要初始化器的非空类型的属性
    private lateinit var myService:MyService

    fun test(){
        myService.performAction()
    }
}

可空类型拓展

Kotlin标准库中定义了String的两个扩展函数isEmptyisBlank
isNullOrBlank,判断是否为空字符串或者是否只包含空白字符,这种扩展函数可以允许接收者为null的调用:

fun  verifyInput(input:String?){
    if (input.isNullOrBlank()){
        print("it is not ok")
    }
}

不需要安全访问,因为函数内部会处理可能的null值:

@kotlin.internal.InlineOnly
public inline fun CharSequence?.isNullOrBlank(): Boolean {
    contract {
        returns(false) implies (this@isNullOrBlank != null)
    }

    return this == null || this.isBlank()
}

类型参数的可空性

Kotlin中所有泛型类和泛型函数的类型参数默认都是可空的,这种情况下,使用类型参数作为类型的声明都允许为null,尽管类型参数T没有用问号结尾。

fun <T> printSomething(t:T){
    // 因为 t 可能为 null,所以必须使用安全调用
    print(t?.hashCode())
}
// 传入 null 进行调用
printSomething(null)

在该方法中,类型参数 T 推导出的类型是可空类型 Any? ,因此尽管没有使用问号结尾,实参 t 仍然允许使用 null。

要是类型参数非空,必须要为它指定一个非空的上界,那样泛型会拒绝可空值作为实参。

// 这样的话,T 就不是可空的
fun <T:Any> printSomething1(t:T){
    print(t.hashCode())
}

is:类型检查和自动转换

is来检查某个对象是不是某个类型,如果确定某个不可变的变量的类型,那后边使用它的时候不用再显式转换:

fun getStringLength(obj: Any): Int? {
	if (obj is String) {
		// 这里obj已经转为String类型
		return obj.length
	}
	return null
}

或者用!is反过来写:

fun getStringLength(obj: Any): Int? {
	if (obj !is String) return null

	return obj.length
}

类和对象

创建一个Person类:

class Person {
	var name = ""
	var age = 0
	fun printInfo() {
		println("name is $name, age is $age")
	}
}

继承

Kotlin中默认的类是不可被继承的(即被final修饰),如果想让这个类能够被继承,需要在class前使用open关键字。

open class Person {
	var name = ""
	var age = 0
	fun printInfo() {
		println("name is $name, age is $age")
	}
}

声明一个Student类继承Person类,kotlin中继承使用:后接父类的构造:

class Student : Person() {
	var number = ""
	var grade = 0
	fun study() {
		println("$name is studying")
	}
}

构造

构造分为主构造和次构造

主构造

主构造直接写在类后边

class Student(val number: String, val grade: Int) : Person() {
	fun study() {
		println("$name is studying")
	}
}

创建一个Student对象:

val student = Student("1234", 90)

因为父类Person还有name和age属性,因此给父类Person也构建一个主构造:

open class Person(val name : String, val age : Int) {
	fun printInfo() {
		println("name is $name, age is $age")
	}
}

此时Student报错,因为继承Person时,后边使用的是Person()无参构造,而上边我们修改了Person的有参构造,则就不存在无参构造了。

class Student(name: String,  age: Int, val number: String, val grade: Int) : Person(name, age){
	fun study() {
		println("$name is studying")
	}
}

此时构造一个Student类;

val student = Student("zjm", 20, "1234", 90)

如果构造时需要特殊处理怎么办?kotlin提供了init结构体,主构造的逻辑可以在init中处理,如:

open class Person(val name : String, val age : Int) {
	init {
		println("name is $name")
		println("age is $age")
	}
}

上述修改都为主构造,如果类需要多个构造怎么办?此时需要借助次构造

次构造

次构造,使用constructor关键字声明构造器,:后边接this(),即次级构造器的本质是调用主构造
实现下边两个次构造:

  1. 三个参数的构造, name,age,number,grade不传参数,默认为0.
  2. 无参构造:Int默认为0, String默认为空字符串
class Student(name: String,  age: Int, var number: String, var grade: Int) : Person(name, age){
    fun study() {
        println("$name is studying")
    }
    constructor(name: String, age: Int, number: String) : this(name, age, number, 0) {

    }
    
    constructor() : this("", 0, "", 0) {
        
    }
}

// 调用
val student = Student("lzy", 23, "1234", 90)
val student1 = Student("lzy", 23, "121");
val student2 = Student()

权限修饰符

Java和kotlin的不同如下表:
在这里插入图片描述
kotlin引入internal,摒弃了default

数据类和单例类

数据类

数据类只处理数据相关,与Java Bean类似,通常需要实现其getsethashcodeequaltoString等方法。

下面实现UserBean,包含:idnamepwd属性。
Java:

public class UserBean {
    private String id;
    private String name;
    private String pwd;

    public UserBean() {

    }

    public UserBean(String id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UserBean userBean = (UserBean) o;
        return Objects.equals(id, userBean.id) && Objects.equals(name, userBean.name) && Objects.equals(pwd, userBean.pwd);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, pwd);
    }

    @Override
    public String toString() {
        return "UserBean{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}

Kotlin编写此类将变的非常简单,新建一个kt文件,选择如下:
在这里插入图片描述
一行代码即可搞定,kotlin会自动实现上述方法。
Shift连按两次调出工具搜索,搜索show kotlin ByteCode,查看kotlin字节码:
确实是创建了相应的Java Bean 类
在这里插入图片描述

单例类

目前Java使用最广的单例模式(静态内部类)实现如下:

public class SingleInstance {
	private SingleInstance() {
	}
	private static class SingleHolder {
		private static final SingleInstance INSTANCE = new SingleInstance();
	}

	public static SingleInstance getInstance() {
		return SingleHolder.INSTANCE;
	}
	public void test() {
	}
}

Kotlin中创建单例类需选择Object
在这里插入图片描述
生成的代码如下:

object SingleInstance {
	fun test () {
	}
}

其对应的java文件如下,和上述使用最多的java单例实现类似:

public final class Singleton {
   @NotNull
   public static final Singleton INSTANCE;

   public final void test() {
   }

   private Singleton() {
   }

   static {
      Singleton var0 = new Singleton();
      INSTANCE = var0;
   }
}

使用如下:

fun main() {
    Singleton.test() //对应的java代码为Singleton.INSTANCE.test();
}

枚举类

枚举类最基本的应用场景是实现类型安全的枚举:

enum class Direction {
    NORTH, SOUTH, WEST, EAST
}

每个枚举常量都是一个对象。枚举常量以逗号分隔。

因为每一个枚举都是枚举类的实例,所以可以这样初始化:

enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF)
}

每个枚举常量也都具有这两个属性:name 与 ordinal, 用于在枚举类声明中获取其名称与(自 0 起的)位置:

enum class RGB { RED, GREEN, BLUE }

fun main() {
    //sampleStart
    println(RGB.RED.name) // prints RED
    println(RGB.RED.ordinal) // prints 0
    //sampleEnd
}

接口

定义一个接口:

interface Study {
    fun study()
    fun readBooks()
    fun doHomeWork()
    fun exercise()
}

接口的继承

继承接口只需在类的后边用,,并实现Study声明的全部函数:

class GoodStudent(name: String, age: Int, var ID: String, var grade: Int) : Person(name, age) , Study{
    override fun study() {
        TODO("Not yet implemented")
    }

    override fun readBooks() {
        TODO("Not yet implemented")
    }

    override fun doHomeWork() {
        TODO("Not yet implemented")
    }

    override fun exercise() {
        TODO("Not yet implemented")
    }

}

Kotlin支持接口方法的默认实现,JDK1.8以后也支持此功能,方法有默认实现,则继承类就不是必须实现此方法。

interface Study {
    fun study() {
        println("study")
    }
    fun readBooks()
    fun doHomework()
}

解决覆盖冲突

实现多个接口时,可能会遇到同一方法继承多个实现的问题:

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 的一个具体类 C,那么必须要重写 bar() 并实现这个抽象方法。

然而,如果从 A 和 B 派生 D,需要实现从多个接口继承的所有方法,并指明 D 应该如何实现它们。这一规则既适用于继承单个实现(bar())的方法也适用于继承多个实现(foo())的方法。

函数式接口(SAM)接口

只有一个抽象方法的接口称为函数式接口或 单一抽象方法(single abstract method , SAM)接口。函数式接口可以有多个非抽象成员,但只能有一个抽象成员。

例如,有这样一个 Kotlin 函数式接口:

fun interface IntPredicate {
   fun accept(i: Int): Boolean
}

如果不使用 SAM 转换,那么你需要像这样编写代码:

// 创建一个类的实例
val isEven = object : IntPredicate {
   override fun accept(i: Int): Boolean {
       return i % 2 == 0
   }
}

通过利用 Kotlin 的 SAM 转换,可以改为以下等效代码:

fun interface IntPredicate {
   fun accept(i: Int): Boolean
}

val isEven = IntPredicate { it % 2 == 0 }

fun main() {
   println("Is 7 even? - ${isEven.accept(7)}")
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/633407.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MindMapper 思维导图 21.x

MindMapper 可视化绘图软件是任何想要快速组织思想和想法的人的必备工具。MindMapper 具有易于使用的界面&#xff0c;可以轻松创建可用于头脑风暴、决策制定和项目管理的综合思维导图。MindMapper 还是一个非常强大的生产力工具&#xff0c;因为它允许用户跟踪他们在项目和任务…

Linux GCC,GDB,Shell脚本的简单使用

这里写目录标题 GCC命令GDB命令Shell脚本 GCC命令 GCC&#xff08;GNU Compiler Collection&#xff0c;GNU编译器套件&#xff09;是由GNU开发的编程语言译器 编译一个简单的.c程序&#xff1a; 四步分开写&#xff1a; gcc -E -o hello.i hello.c // 预处理 gcc -S -o he…

49、基于51单片机无刷电机调速系统设计(程序+原理图+PCB图+英文文献+参考论文+开题报告+元器件清单等)

摘 要 由于电机的发展&#xff0c;电动机由之前的带有刷子变换为无刷子直流电动机&#xff0c;其内部结构是电子换向器&#xff0c;而不是传统的机械式换向器&#xff0c;电动机的组成为永磁材料制造的转子和带有线圈绕组的定子组成。 所以本论文思路在全面分析无刷直流电机…

Tuleap ,一个用于软件项目管理的平台

Eclipse 基金会使用 Tuleap 取代了 Bugzilla,Tuleap 是一个独特的开源项目管理工具&#xff0c;目前发展势头很好&#xff0c;现在&#xff0c;每个月它会出一个大版本。它还被列在2015 年五大开源项目管理工具和 2016 年十一个名列前茅项目管理工具中。 Manuel Vacelet 是开发…

【MySQL】一文带你了解MySQL的基础知识

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集&#xff01; &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指…

【Docker】docker部署springboot+vue+mysql+nginx前后端分离项目【实战篇】

文章目录 1、docker 安装jdk2、docker 安装mysql通过Docker命令进入Mysql容器内部初始化数据sqlDbx连接查看 3、docker build构建后端镜像修改配置数据库JDBC链接IP为虚拟机服务器IPmaven clean package打包后端jar并上传到服务器编写Dockfilebuild 构建查看构建的后端镜像app …

51单片机读取DS18B20温度传感器

1.首先我们知道DS18B20是单总线协议&#xff0c;只有一根数据线。所以Data数据线即使发送端又是接收端&#xff0c;同时DS18B20内部接了弱上拉电阻&#xff08;如图一所示&#xff09;&#xff0c;数据线默认为高电平。有了这些概念&#xff0c;我们就能进行下一步。 图一&…

1738_创建自己的simulink模块库

全部学习汇总&#xff1a; GreyZhang/g_matlab: MATLAB once used to be my daily tool. After many years when I go back and read my old learning notes I felt maybe I still need it in the future. So, start this repo to keep some of my old learning notes servral …

随着网络犯罪策略的转变,暗网威胁瞄准能源行业

根据 Searchlight Cyber 的一份报告&#xff0c;能源行业越来越多地通过暗网上的活动成为恶意行为者和威胁组织的目标。 该报告详细介绍了威胁行为者出售全球能源组织初始访问权的众多实例。 其中包括美国、加拿大、英国、法国、意大利和印度尼西亚的流行暗网论坛&#xff08…

使用AIGC工具巧用Linux系统

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

【Python爬虫开发基础③】Python基础(文件操作方法汇总)

友情提示&#xff1a;由于本专栏的文章偏向于爬虫&#xff0c;所以对于python的介绍不可能面面俱到&#xff0c;在这里只讲重点。 如果大家觉得有没讲到的地方&#xff0c;欢迎补充~ 往期推荐&#xff1a; 【Python爬虫开发基础①】Python基础&#xff08;变量及其命名规范&…

黑马Redis视频教程高级篇(三:最佳实践)

目录 一、Redis键值设计 1.1、优雅的key结构 1.2、拒绝BigKey 1.2.1、BigKey的危害 1.2.2、如何发现BigKey 1.2.3、如何删除BigKey 1.3、恰当的数据类型 例1&#xff1a;比如存储一个User对象&#xff0c;我们有三种存储方式&#xff1a; 例2&#xff1a;假如有hash类…

5.部署LVS-DR群集

文章目录 部署LVS-DR群集LVS-DR数据包流向分析LVS-DR中的ARP问题问题一问题二 LVS-DR部署调度器配置节点服务器配置NFS服务器配置 ipvsadm 部署LVS-DR群集 LVS-DR数据包流向分析 为方便进行原理分析&#xff0c;将Client与群集机器放在同一网络中&#xff0c;数据包流经的路线…

2000-2019年280多个地级市就业人数数据(全市)/城市就业数据

2000-2019年280多个地级市就业人数数据&#xff08;全市&#xff09; 1、时间范围&#xff1a;2000-2019年 2、范围&#xff1a;包括280多个地级市 3、来源&#xff1a;城市NJ 4、统计口径&#xff1a;全市 5、单位&#xff1a;万人 6、缺失情况&#xff1a;与城市年鉴缺失情况…

python学习笔记20230611

python语言的特点 python是脚本语言&#xff0c; 采用解释方式执行。 解释是将源代码逐条转换成目标代码同时逐条运行目标代码的过程。执行解释的计算机程序就是解释器。 解释执行方式是逐条运行用户编写的代码&#xff0c;执行性能略低&#xff0c;但他支持跨硬件或操作系统…

HNU计算机图形学-作业二

HNU计算机图形学-作业二 作业二&#xff1a;纹理和照明前言介绍实施详细信息任务1&#xff1a;加载复杂对象任务2&#xff1a;纹理映射和照明任务3&#xff1a;互动活动和动画额外任务&#xff1a;增强场景的视觉效果&#xff08;最高20%&#xff09; 最终实现效果 作业二&…

【Protobuf速成指南】oneof关键字的使用

文章目录 2.3 oneof 字段一、基本认识二、 3.2 oneof相关函数三、Contact2.3 改写 2.3 oneof 字段 本系列文章将通过对通讯录项目的不断完善&#xff0c;带大家由浅入深的学习Protobuf的使用。这是Contacts的2.3版本&#xff0c;在这篇文章中将带大家学习Protobuf的 oneof 语关…

LVS负载均衡集群-NAT模式

目录 前言一、企业群集应用概述1. 群集的含义1.1 群集的特点1.2 扩展服务器的方式 2. 群集的类型2.1 负载均衡群集&#xff08;Load Balance Cluster&#xff09;2.2 高可用群集&#xff08;High Availability Cluster&#xff09;2.3 高性能运算群集&#xff08;High Performa…

STL——stack容器、queue容器、list容器

初识STL **stack容器****stack容器——基本概念****stack容器——常用接口** **queue容器****queue容器——基本概念****queue容器——常用接口** **list容器****list容器基本概念****list容器——构造函数****list容器——赋值和交换****List容器——大小操作****list容器——…

碳排放预测模型 | Python实现基于机器回归分析的碳排放预测模型——数据可视化和探索

文章目录 效果一览文章概述研究内容环境准备源码设计学习总结参考资料效果一览 文章概述 碳排放预测模型 | Python实现基于机器回归分析的碳排放预测模型——数据可视化和探索 目标是测试所选特征对分析的重要性,检测异常值的存在并准备数据以供进一步分析。 </