当前位置: 首页 > news >正文

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

文章目录

  • 认识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() {
//sampleStartval a: Int = 100val boxedA: Int? = aval anotherBoxedA: Int? = aval b: Int = 10000val boxedB: Int? = bval anotherBoxedB: Int? = bprintln(boxedA === anotherBoxedA) // trueprintln(boxedB === anotherBoxedB) // false
//sampleEnd
}

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

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

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

显式数字转换

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

fun main() {val b : Byte = 1val 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.0val 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() {
//sampleStartthrow 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?.cityreturn 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?.cityreturn 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 falsereturn 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?=nullfun test() {myService!!.performAction()}
}

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

class MyService{fun performAction():String = "foo"
}class MyTest{// 声明一个不需要初始化器的非空类型的属性private lateinit var myService:MyServicefun 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 nullreturn obj.length
}

类和对象

创建一个Person类:

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

继承

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

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

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

class Student : Person() {var number = ""var grade = 0fun 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;}@Overridepublic 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);}@Overridepublic int hashCode() {return Objects.hash(id, name, pwd);}@Overridepublic 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 {@NotNullpublic 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() {//sampleStartprintln(RGB.RED.name) // prints REDprintln(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)}")
}

相关文章:

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

文章目录 认识Kotlin跨平台特性语言类型java的语言类型kotlin的运行原理 hello world 基本概念程序入口数据与对象类型 和 显式数字转换浮点类型位运算AnyUnitNothing 声明变量只读变量 val与可变变量var查看Kotlin字节码 fun&#xff08;方法 / 函数&#xff09;函数参数默认值…...

【Linux】Docker部署镜像环境 (持续更新ing)

防火墙 1、查看防火墙状态 sudo systemctl status ufw 2、开启防火墙 sudo systemctl start ufw 3、关闭防火墙 sudo systemctl stop ufw 4、开机禁止开启防火墙 sudo systemctl disabled ufw 5、开启自启防火墙 sudo systemctl enabled ufw Elasticsearch 1、安装指定版本 比…...

Jtti:如何打开云服务器的8082端口

如何打开云服务器的8082端口? 第一步&#xff1a;登录云服务器 首先&#xff0c;我们需要登录到我们的云服务器。可以使用SSH、控制台等方式进行登录。登录成功后&#xff0c;我们可以在终端上看到服务器的控制台。 第二步&#xff1a;编辑防火墙规则 打开终端后&#xff0c;我…...

有关 string 类的练习(下)

目录 一、反转字符串 II 二、反转字符串中的单词 III 三、找出字符串中第一个只出现一次的字符 四、字符串相乘 五、把字符串转换成整数 一、反转字符串 II 给定一个字符串 s 和一个整数 k&#xff0c;从字符串开头算起&#xff0c;每计数至 2k 个字符&#xff0c;就反转…...

XuperChain搭建+报错+注意事项

安装依赖 golang 这里安装的是15-17版本 wget -c https://dl.google.com/go/go1.15.2.linux-amd64.tar.gz -O - | sudo tar -xz -C /usr/local 添加环境变量 这个可以通过添加下面的行到/etc/profile文件(系统范围内安装)或者$HOME/.profile文件(当前用户安装 vim /etc…...

【伏羲八卦图】(PythonMatlab实现)

目录 1 与达尔文对话 2 与老子对话 2.1 Python实现 2.2 Matlab实现 1 与达尔文对话 140年前&#xff0c;1858年7月1日&#xff0c;达尔文在英伦岛发表了自己有关自然选择的杰出论文。他提出&#xff0c;生物的发展规律是物竞天择。经过物竞&#xff0c;自然界选择并存留最具…...

ruoyi数据权限学习

思路 用户关联了角色&#xff08;用户可以关联多个角色&#xff09;&#xff0c;给角色设置数据权限分类&#xff0c;数据权限分类有如下5种&#xff1a; 全部数据权限 - DATA_SCOPE_ALL自定数据权限 - DATA_SCOPE_CUSTOM部门数据权限 - DATA_SCOPE_DEPT部门及以下数据权限 -…...

WPF中实现动态导航

主页面 <mah:MetroWindowx:Class"Kx.View.MyMainView"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expression/bl…...

day16 | 104.二叉树的最大深度、111.二叉树的最小深度、 222.完全二叉树的节点个数

目录&#xff1a; 链接 题目链接&#xff1a; https://leetcode.cn/problems/maximum-depth-of-binary-tree/ https://leetcode.cn/problems/maximum-depth-of-n-ary-tree/ https://leetcode.cn/problems/minimum-depth-of-binary-tree/description/ 解题及思路学习 104…...

Spring Boot + Vue3前后端分离实战wiki知识库系统<八>--分类管理功能开发二

接着上一次Spring Boot Vue3 前后端分离 实战 wiki 知识库系统&#xff1c;七&#xff1e;--分类管理功能开发的分类功能继续完善。 分类编辑功能优化&#xff1a; 概述&#xff1a; 现在分类编辑时的界面长这样&#xff1a; 很明显目前的父分类的展现形式不太人性&#xf…...

Python入门(十八)类(一)

类&#xff08;一&#xff09; 1.面向对象概述2.创建和使用类2.1 创建dog类2.2 根据类创建实例2.3 创建多个实例 1.面向对象概述 面向对象编程是最有效的软件编写方法之一。在面向对象编程中&#xff0c;你编写表示现实世界中的事物和情景的类&#xff0c;并基于这些类来创建对…...

c# 从零到精通-定义一个结构

c# 从零到精通-定义一个结构 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Test01 { class Program { public struct Rect//定义一个矩形结构 { public double width;//矩形的宽 public double height;//矩形的高 /// …...

检信ALLEMOTION非接触式心理情绪测评系统

1 名称&#xff1a;检信ALLEMOTION多维度心理情绪测评系统 2 用途&#xff1a;用于群体性人群心理情绪早期筛查&#xff0c;以及个人心理障碍辅助诊断,同时传统心理量表诞生已经100多年历史&#xff0c;在人工智能及大数据推动下&#xff0c;必然推动心理健康行业的产业变革与…...

20道嵌入式经典面试题(附答案)

1.嵌入式系统中经常要用到无限循环&#xff0c;如何用C编写死循环 答&#xff1a;while(1){} 或者 for(;;) 2.程序的局部变量存在于哪里&#xff0c;全局变量存在于哪里&#xff0c;动态申请数据存在于哪里。 答&#xff1a;程序的局部变量存在于栈区&#xff1b;全局变量存在…...

python学习-代码调试器

目录 为什么学习调试器Pycharm Debugger示例所用代码布局调试工具栏 Debug Bar程序控制工具栏 pdb查看源代码 l list查看当前函数源代码 ll longlist打印变量 p查看调用栈w where向上移动当前帧 u up向上移动当前帧 d down运行当前行代码,在第一个可以停止的位置停下 s step继续…...

第十一章 综合推理

第十一章 综合推理 第一节 综合推理-排序 题-综合推理-分类1-排序 甲、乙、丙、丁四人的国籍分别为英国、俄国、法国、日本。乙比甲高&#xff0c;丙更矮&#xff1b;英国人比俄国人高&#xff0c;法国人最高&#xff1b;日本人比丁高。 这四个人的国籍是&#xff1a; A.甲…...

嵌入式开发之设置寄存器中指定位

0 Preface/Foreword 嵌入式开发&#xff0c;位操作是常用的运算&#xff0c;读写对应寄存器指定位从而设置不同的功能。 1 设置寄存器中的任意位 1.1 清零 举例&#xff0c;假设一个寄存器名字为FUNCCON&#xff0c;地址为0x00008000,该寄存器长度为4个byte。 #define FUNC…...

第十章 数学相关

第十章 数学相关 第一节 集合 真题&#xff08;2010-53&#xff09;-数学相关-集合-画饼集能力-朴素逻辑 53.参加某国际学术研讨会的 60 名学者中&#xff0c;亚裔学者 31 人&#xff0c;博士 33 人&#xff0c;非亚裔学者中无博士学位的 4 人。根据上述陈述&#xff0c;参…...

数据结构——串(字符串)

文章目录 **一 串的定义和实现****1 定义****2 串的存储结构****2.1 定长顺序存储表示****2.2 堆分配存储表示****2.3 块链存储表示** **3 串的基本操作** **二 串的模式匹配****1 简单的模式匹配算法****2 串的模式匹配算法——KMP算法****2.1 字符串的前缀&#xff0c;后缀和…...

Seata服务端的启动过程 学习记录

1.ServerRunner ServerRunner类实现了CommandLineRunner与DisposableBean接口&#xff0c;将会在Spring容器启动和关闭的时间&#xff0c;分别执行 run 和 destory 方法。 而seata服务端的启动过程&#xff0c;都藏在run方法中 2.整体流程 io.seata.server.Server#start pu…...

Log4J

引言 为什么要用日志? --> 方便调试代码 什么时候用?什么时候不用? ​ 出错调试代码时候用 生产环境下就不需要,就需要删除 怎么用? --> 输出语句 一、Log4J 1.1 介绍 ​ log4j是Apache的一个开放源代码的项目&#xff0c;通过使用log4j&#xff0c;我们可以控…...

【零基础学机器学习 5】机器学习中的分类:什么是分类以及分类模型

&#x1f468;‍&#x1f4bb; 作者简介&#xff1a;程序员半夏 , 一名全栈程序员&#xff0c;擅长使用各种编程语言和框架&#xff0c;如JavaScript、React、Node.js、Java、Python、Django、MySQL等.专注于大前端与后端的硬核干货分享,同时是一个随缘更新的UP主. 你可以在各个…...

目标检测算法:Faster-RCNN论文解读

目标检测算法&#xff1a;Faster-RCNN论文解读 前言 ​ 其实网上已经有很多很好的解读各种论文的文章了&#xff0c;但是我决定自己也写一写&#xff0c;当然&#xff0c;我的主要目的就是帮助自己梳理、深入理解论文&#xff0c;因为写文章&#xff0c;你必须把你所写的东西表…...

基于Python的接口自动化-Requests模块

目录 引言 一、模块说明 二、Requests模块快速入门 1 发送简单的请求 2 发送带参数的请求 3 定制header头和cookie 4 响应内容 5 发送post请求 6 超时和代理 三、Requests实际应用 引言 在使用Python进行接口自动化测试时&#xff0c;实现接口请求…...

Vue框架中监测数组变化的方法

在 Vue 中&#xff0c;如果直接对数组进行操作&#xff0c;比如使用下标直接修改元素&#xff0c;数组长度不变时&#xff0c; Vue 是无法监测到这种变化的&#xff0c;导致无法触发视图更新。针对该问题&#xff0c;总结如下解决方法&#xff1a; 一、使用 Vue.js 提供的方法…...

PHP isset()函数使用详解,PHP判断变量是否存在

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 isset 一、判断变量是否存在二、判断变量是否为NUL…...

2021~2022 学年第二学期《信息安全》考试试题(A 卷)

北京信息科技大学 2021~2022 学年第二学期《信息安全》考试试题&#xff08;A 卷&#xff09; 课程所在学院&#xff1a;计算机学院 适用专业班级&#xff1a;计科1901-06&#xff0c;重修 考试形式&#xff1a;(闭卷) 一、选择题&#xff08;本题满分10分,共含10道小题,每小题…...

通俗讲解元学习(Meta-Learning)

元学习通俗的来说&#xff0c;就是去学习如何学习&#xff08;Learning to learn&#xff09;,掌握学习的方法&#xff0c;有时候掌握学习的方法比刻苦学习更重要&#xff01; 下面我们进行详细讲解 1. 从传统机器学习到元学习 传统的机器学中&#xff0c;我们选择一个算法&…...

生成全球定位系统、伽利略和北斗二号的Matlab代码及实际数据捕获文件,为测试功能提供完整信号与频谱

使用Matlab生成和分析GNSS信号&#xff08;第一部分&#xff09; 全球导航卫星系统(Global Navigation Satellite System, GNSS)是一个提供全球覆盖的&#xff0c;定位、导航、时间传递服务的系统。由全球定位系统(GPS)&#xff0c;俄罗斯的格洛纳斯(GLONASS)&#xff0c;欧洲…...

Android 14 版本变更总览

Android 14 版本 Android 14 总览Android 14 功能和变更列表行为变更&#xff1a;所有应用行为变更&#xff1a;以 Android 14 或更高版本为目标平台的应用功能和 API 概览 Android 14 总览 https://developer.android.google.cn/about/versions/14?hlzh-cn 文章基于官方资料…...

推广下载/关于华大18年专注seo服务网站制作应用开发

我已经阅读了许多关于使用Apache POI的大文件问题的文章和主题 . 但我对任何解决方案仍有一些问题 . 所以我的任务是从许多Excel文件中删除空行 . 它们被安排在目录的树结构中&#xff0c;我想要立即执行所有程序 . 为此&#xff0c;我有下一个方法&#xff1a;public static v…...

建站工具论坛/网站文章优化技巧

...

做微信平台网站需要多少钱/微博推广方式

实现同一个接口的不同的类,根据调用总和类中静态类型(返回值同接口)方法,(传入不同值),根据if else判定该new哪一个实现类.从而使用不同的接口的实现方法. 将编程看作是一门艺术&#xff0c;而不单单是个技术。 敲打的英文字符是我的黑白琴键&#xff0c; 思维图纸画出的是我编…...

哪里有网站建设哪家好/seo报告

主要的加载顺序是 servletcontext-------->context-param---------->listener---->filter----->servlet 而同一个类别之间的实际情况调用顺序要根据鬼影的mapping的顺序进行调用。 转载于:https://www.cnblogs.com/mengzhongyunying/p/8668311.html...

三亚网站建设兼职/怎么创建自己的网站平台

App在后台久置后&#xff0c;再次从桌面或最近的任务列表唤醒时经常会发生崩溃&#xff0c;这往往是App在后台被系统杀死&#xff0c;再次恢复的时候遇到了问题&#xff0c;而在使用FragmentActivityFragment的时候会更加频繁。比如&#xff0c;如果Fragment没有提供默认构造方…...

杭州电子网站建设方案/百度浏览器打开

基本数据类型python的基本数据类型如下:1. int > 整数. 主要用来进行数学运算2. str > 字符串, 可以保存少量数据并进行相应的操作3. bool>判断真假, True, False4. list> 存储大量数据.用[ ]表示5. tuple> 元组, 不可以发生改变 用( )表示6. dict> 字典, 保…...